在C#中从服务器向客户端发送一些约束条件

6
我使用C#中的socket编程创建了一个简单的服务器,它将从客户端接收文件。下面是我的示例代码段。
我想添加一些限制。我希望对文件大小(如4KB或2KB)和允许的文件格式(如.doc、.txt、.cpp等)进行限制,并在客户端连接到服务器时将其发送给客户端,以便客户端可以相应地发送文件。我该怎么做?
示例代码段:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

namespace FileTransfer
{
    class Program
    {
        static void Main(string[] args)
        {
            // Listen on port 1234

            TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234);
            tcpListener.Start();

            Console.WriteLine("Server started");

            //Infinite loop to connect to new clients
            while (true)
            {
                // Accept a TcpClient
                TcpClient tcpClient = tcpListener.AcceptTcpClient();
                                Console.WriteLine("Connected to client");
                byte[] data = new byte[1024];
                NetworkStream ns = tcpClient.GetStream();
                int recv = ns.Read(data, 0, data.Length);
                StreamReader reader = new StreamReader(tcpClient.GetStream());

               //Will add some lines to add restrictions...

            }
        }
    }
}

我需要在代码中添加哪些额外的行来将限制发送给客户端?


1
为了限制文件大小,只需添加总数据大小计数器,例如“int counter = 0; counter += recv;”。然后,如果超出限制,则使用适当的消息丢弃客户端。 - user2440074
我只想在客户端连接时立即向客户端发送文件大小和允许的文件格式,以便客户端可以相应地发送文件。@Alek Depler - user5379550
嗯,那么你就需要创建自己的客户端-服务器协议。客户端和服务器都可以相互发送数据,你需要将所有数据分为两种类型:技术协议指令和数据本身。 "CodeCaster" 的答案是正确的。 - user2440074
2个回答

4

基本上,我认为你需要两件事:

  • 按照其他答案中的建议定义应用程序协议

  • 处理部分读/写

对于处理部分读取(不确定在write中需要多少此类函数),可以使用类似下面的函数:

public static void ReadWholeArray (Stream stream, byte[] data)
{
    int offset=0;
    int remaining = data.Length;
    while (remaining > 0)
    {
        int read = stream.Read(data, offset, remaining);
        if (read <= 0)
            throw new EndOfStreamException 
                (String.Format("End of stream reached with {0} bytes left to read", remaining));
        remaining -= read;
        offset += read;
    }
}

事实上,传统的Stream.Read()方法并不能保证读取您告诉它的字节数,而这个方法则可以确保读取与data.Length参数指定的字节数相同。因此,您可以使用这样的函数来实现所需的应用程序协议。
一些相关的应用程序协议信息可以在这里找到。
好的,下面是服务器如何发送文件长度限制和文件扩展名的示例:
// Send string
string ext = ".txt";
byte [] textBytes = Encoding.ASCII.GetBytes(ext);
ns.Write(textBytes, 0, textBytes.Length); 

// Now, send integer - the file length limit parameter
int limit = 333;
byte[] intBytes = BitConverter.GetBytes(limit);
ns.Write(intBytes, 0, intBytes.Length); // send integer - mind the endianness

但你仍需要某种协议,否则应该让客户端读取“完整”流并稍后以某种方式解析这些数据,如果数据没有固定长度等,则这并不容易-否则客户端如何区分消息的哪个部分是文本、哪个部分是整数?


因为链接到Beej的指南,所以你获得了一枚赞。任何对网络编程感兴趣的人都应该至少阅读过那个指南。 - CodeCaster
@CodeCaster:是的,确实,谢谢。那个指南包含了有用的信息。OP需要处理部分发送/接收,并考虑一些协议,比如你建议的TLV等。 - Giorgi Moniava
但是我想在客户端连接时将约束条件发送给客户端,以便它可以根据约束条件发送文件。我想从客户端而不是服务器端进行检查。从服务器端,我只想发送约束条件 @Giorgi - user5379550
能否同时发送多个文件扩展名?@Giorgi - user5379550
@user5379550:没问题,因为你正在发送字节数组。所以在接收端,你只需要区分哪个是文件扩展名,哪个是文件长度限制等等。阅读另一个答案中提到的TLV,了解要发送什么以及如何在客户端上读取它。在我的示例中,该字符串也可以包含多个文件扩展名,例如用“;”分隔。但是用户仍然必须首先知道该字符串的长度,因此可能需要提前发送消息的前两个字节作为长度。 - Giorgi Moniava
显示剩余3条评论

3
您似乎犯了经典的套接字错误。所提供的代码和解释似乎假定套接字处理消息。它们不是这样的。使用这种方式时,您正在使用流式互联网套接字,它们提供流而不是消息
您没有展示任何实际发送数据的代码,所以我猜想您只是将文件数据传输到另一端并关闭连接。否则,您如何知道已成功传输整个文件?
客户端和服务器必须遵循一组规则才能通过套接字有用地交换数据,称为应用程序协议。您必须拥有一个,否则您只会将数据发送到某个位置,并且完全无法控制。这意味着服务器和客户端都不知道发生了什么,他们只会发送和接收数据,希望一切顺利。因此,您需要重构代码,而不是只需添加“几行”代码。

有很多方法可以定义应用程序协议,也有许多选项可供选择,所以我将展示一个任意的方法:文本解释消息,这些消息带有ID和有效载荷长度(如果适用),两者都是未指定的数字变量。例如,您可以选择小端四字节无符号整数。

以这种格式编写的消息被称为 "Type/Length/Value" 或 TLV

因此,我们定义这些消息:

ID  Name         Direction         Description                    Payload
1   ServerHello  Server -> Client  The server sends this message  None. 
                                   to every connecting client.    Or maybe server or 
                                                                  protocol version.
2   MaxUpload    Server -> Client  Sent after the ServerHello.    Maximum upload size 
                                                                  in bytes.
3   AllowedExts  Server -> Client  Allowed upload extensions,     The allowed extensions. 
                                   comma-separated. Sent after 
                                   MaxUpload message.
10  IncomingFile Client -> Server  There's a file coming.         The file name.
11  FileUpload   Client -> Server  The file to upload.            The file data.
                                   Sent after IncomingFile.

现在只需要在服务器和客户端中实现此应用程序协议即可完成。
您还需要决定如何处理不遵循协议的客户端或服务器。例如,它可以发送一个无法解析的消息、未知的消息ID、您不想支持的消息长度、乱序的消息(FileUpload之前的IncomingFile)或与先前发送的消息不一致的消息,如客户端上传比服务器接受的文件更大或无效的扩展名。您还需要考虑“确认”或响应消息,例如服务器告诉客户端“好的,继续发送下一条消息”。
总的来说,这是一个非常广泛的问题,不容易回答。我试图在您的问题评论中回答这个问题,但被删除了。所以在这里你有你的答案。

你可以在网络上了解更多相关信息,例如 Beej的网络编程指南Giorgi提供链接(务必阅读整个指南)和Stephen Cleary的博客


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接