服务器到客户端的小文件传输






4.89/5 (19投票s)
这个小型应用程序将帮助您获得有关如何将文件从服务器发送到客户端的基本知识。
引言
这个小型应用程序将帮助您获得有关如何将文件从服务器发送到客户端的基本知识。
背景
之前我发表了一篇关于如何将文件从客户端传输到服务器的文章,现在将发布关于文件从服务器传输到客户端的文章。通过将这两者结合起来,您可以编写关于客户端之间传输文件的代码。
关于学习套接字编程的完整系列可在我的博客 Socket Programming in C# 上找到。您可以在那里找到更多关于使用 C# 进行套接字和分布式架构编程的文章。
之前我看到用户因为我的英语不好而遇到问题,现在我正努力改进这方面。我相信现在您不会在我的英语方面遇到太多问题了。
Using the Code
要将文件从服务器发送到客户端,必须有两个应用程序,即服务器应用程序和客户端应用程序。在代码中,我将这两个部分单独进行了说明。下面的部分我将描述服务器操作,即服务器应用程序正在工作,您需要检查服务器代码;对于客户端操作,需要检查客户端代码。这两个套接字编程应用程序的握手应遵循以下步骤:
1) 服务器操作:首先需要运行服务器应用程序,该服务器应用程序将使用预定义的 IP 地址和端口号打开一个终结点,并保持监听模式以接受来自客户端的新套接字连接请求。
这就像有人在一个固定位置等待着回复某人的请求一样。
服务器应用程序的以下代码部分正是做同样的事情。
IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, 5656); Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); sock.Bind(ipEnd); sock.Listen(100);
这里,第 1 行使用端口号 5656 和本地计算机 IP 地址创建了一个 IP 终结点。端口号可以是任何大于 1024 的数字,除了保留端口号。
接下来的两行代码创建了一个套接字对象,并将其绑定到先前创建的 IP 终结点。
最后一行将新创建的套接字对象设置为监听模式,以接受来自客户端的新连接请求。
所以您需要先运行服务器应用程序,然后再运行客户端应用程序。
2) 客户端操作:现在轮到客户端向服务器发出请求了。
IPAddress[] ipAddress = Dns.GetHostAddresses("localhost"); IPEndPoint ipEnd = new IPEndPoint(ipAddress[0], 5656); Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); clientSock.Connect(ipEnd);
这些代码来自客户端应用程序,这里的前两行用于获取本地主机 IP 地址,并使用它创建一个新的 IP 终结点。请确保这里的 IP 地址和端口号必须与服务器地址相同。我在同一台机器上运行我的应用程序,所以使用了 localhost。
接下来的两行代码创建一个套接字对象,并通过 IP 终结点尝试连接。所以这个套接字对象将尝试连接处于监听模式的服务器套接字。
3) 服务器操作:再次轮到服务器响应客户端的请求,这由服务器应用程序中的以下代码行完成:
Socket clientSock = sock.Accept();
这里的“sock”是之前创建的服务器套接字对象,它处于监听模式。这个 sock 对象将接受客户端请求并生成一个名为“clientSock”的新套接字对象。服务器端的其余所有工作将由“clientSock”对象执行,以处理此特定的客户端请求。
4) 服务器操作:这些代码与套接字编程没有直接关系。它们用于读取和发送文件到客户端。
string fileName = "test.txt";// "Your File Name"; string filePath = @"C:\FT\";//Your File Path; byte[] fileNameByte = Encoding.ASCII.GetBytes(fileName); byte[] fileData = File.ReadAllBytes(filePath + fileName); byte[] clientData = new byte[4 + fileNameByte.Length + fileData.Length]; byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length); fileNameLen.CopyTo(clientData, 0); fileNameByte.CopyTo(clientData, 4); fileData.CopyTo(clientData, 4 + fileNameByte.Length);
这些代码行从本地驱动器读取某个特定文件,并将其数据存储在字节数组“clientData”中。文件数据需要以原始字节格式存储在数组中,以便发送给客户端。在数据之前,还包含文件名和大小。这在客户端和服务器之间是预定义的,否则客户端将无法获取服务器发送的文件名。
在我的例子中,我使用前四个字节表示文件名长度,从第五个字节开始存储文件名。所以所有文件数据都将存储在文件名之后。
5) 服务器操作:现在文件数据已在字节数组中,需要将其发送给客户端。使用客户端套接字(clientSock)对象(在接受客户端请求时创建)通过以下代码完成了同样的事情。
clientSock.Send(clientData);
基本上,服务器应用程序在此处的小文件传输任务就结束了。其余代码用于一些装饰和套接字关闭相关的事情。
5) 客户端操作:现在再次轮到客户端,它将执行以下任务。
byte[] clientData = new byte[1024 * 5000]; string receivedPath = "C:/"; int receivedBytesLen = clientSock.Receive(clientData);
这里的前两行只是创建字节数组来存储服务器数据,并使用路径来决定数据保存的位置。在我的代码中,我将数据保存在 C: 驱动器中。
最后一行开始从服务器接收数据。当客户端套接字开始接收服务器数据时,它会在一个整数变量中返回捕获到的数据长度。
6) 客户端操作:此部分代码检索文件名长度,并使用此长度从服务器在文件数据开头发送的文件名。这将需要检索文件名。
int fileNameLen = BitConverter.ToInt32(clientData, 0); string fileName = Encoding.ASCII.GetString(clientData, 4, fileNameLen);
7) 客户端操作:现在接收到的数据将使用以下代码行,通过二进制流写入器保存在客户端。
BinaryWriter bWrite = new BinaryWriter(File.Open(receivedPath + fileName, FileMode.Append)); bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);
这里,文件数据开始在文件大小和文件名字节之后进行检索。这在第二行中进行了管理。
这样,一个小文件就可以从服务器发送到客户端了。
8) 客户端和服务器操作:现在服务器和客户端都将执行相同的活动,即使用套接字的 close 方法释放服务器和客户端套接字。客户端还需要关闭二进制流写入器。
bWrite.Close(); clientSock.Close();
值得关注的点
通过遵循这些步骤,文件就可以从服务器发送到客户端。同样,我们可以从服务器发送大文件到客户端。TCP 缓冲区一次无法处理大量数据。所以如果您尝试发送大文件,它会抛出溢出错误。要避免此错误,您需要将大文件分割成小块,然后逐块发送。因此,会有一个循环用于从服务器到客户端发送文件,这意味着步骤 5 到步骤 7 会重复。
服务器可以根据客户端的请求发送特定的文件。但是,为此,客户端需要在服务器请求时发送请求文件的信息。服务器可以根据客户端的请求搜索文件。
通过处理多个客户端对象,一个服务器可以同时将文件发送给多个客户端,但为此您需要创建一个多线程应用程序,并需要跟踪带有文件数据的客户端套接字对象数组。所以编程会更复杂,但基本原理是相同的。