使用套接字(C#)进行文件传输 – 接收的文件不包含完整数据

我使用套接字连接创建了一个服务器和一个客户端进行文件传输。 我面临的问题是,如果收到的文件大小超过8KB,则该文件不完整。

如果你遇到这个问题,你可以指导我找出我做错了什么(在服务器/客户端)?

以下是两种方法:

客户:

#region FILE TRANSFER USING C#.NET SOCKET - CLIENT class FTClient { public static string curMsg_client = "Idle"; public static void SendFile(string fileName) { try { //IPAddress[] ipAddress = Dns.GetHostAddresses("localhost"); //IPEndPoint ipEnd = new IPEndPoint(ipAddress[0], 5656); string IpAddressString = "192.168.1.102"; IPEndPoint ipEnd_client = new IPEndPoint(IPAddress.Parse(IpAddressString), 5656); Socket clientSock_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); string filePath = ""; fileName = fileName.Replace("\\", "/"); while (fileName.IndexOf("/") > -1) { filePath += fileName.Substring(0, fileName.IndexOf("/") + 1); fileName = fileName.Substring(fileName.IndexOf("/") + 1); } byte[] fileNameByte = Encoding.UTF8.GetBytes(fileName); if (fileNameByte.Length > 5000 * 1024) { curMsg_client = "File size is more than 5Mb, please try with small file."; return; } curMsg_client = "Buffering ..."; string fullPath = filePath + fileName; byte[] fileData = File.ReadAllBytes(fullPath); 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); curMsg_client = "Connection to server ..."; clientSock_client.Connect(ipEnd_client); curMsg_client = "File sending..."; clientSock_client.Send(clientData, 0, clientData.Length, 0); curMsg_client = "Disconnecting..."; clientSock_client.Close(); curMsg_client = "File [" + fullPath + "] transferred."; } catch (Exception ex) { if (ex.Message == "No connection could be made because the target machine actively refused it") curMsg_client = "File Sending fail. Because server not running."; else curMsg_client = "File Sending fail." + ex.Message; } } } #endregion 

服务器:

 #region FILE TRANSFER USING C#.NET SOCKET - SERVER class FTServer { IPEndPoint ipEnd_server; Socket sock_server; public FTServer() { //make IP end point to accept any IP address with port 5656 //these values will be altered depending on ******* (and/or other ImportSystem) //this was initially coded, but threw a SocketException on sock.Bind(ipEnd) {Only one usage of each socket address (protocol/network addres/port) is normally permitted} //ipEnd = new IPEndPoint(IPAddress.Any, 5656); // //I'll set it like this (giving the IP through a string) string IpAddressString = "192.168.1.102"; ipEnd_server = new IPEndPoint(IPAddress.Parse(IpAddressString), 5656); // //creating new socket object with protocol type and transfer data type sock_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); //bind end point with newly created socket sock_server.Bind(ipEnd_server); } public static string receivedPath = @"C:\Users\Adrian.Constantin\Desktop\Simulare_Forder_Import\"; public static string curMsg_server = "Stopped!"; public void StartServer() { try { curMsg_server = "Starting..."; sock_server.Listen(100); curMsg_server = "Running and waiting to receive file."; Socket clientSock = sock_server.Accept(); byte[] clientData = new byte[512000]; int receivedBytesLen = clientSock.Receive(clientData, SocketFlags.None); clientSock.ReceiveBufferSize = 8192; curMsg_server = "Receiving data..."; int fileNameLen = BitConverter.ToInt32(clientData, 0); string fileName = Encoding.UTF8.GetString(clientData, 4, fileNameLen); BinaryWriter bWrite = new BinaryWriter(File.Open(receivedPath + "/" + fileName, FileMode.OpenOrCreate)); ; bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen); curMsg_server = "Saving file..."; bWrite.Close(); clientSock.Close(); curMsg_server = "Received and Archived file [" + fileName + "]; Server stopped."; } catch (SocketException ex) { curMsg_server = "File Receving error."; MessageBox.Show(String.Format("{0} Error cide: {1}", ex.Message, ex.ErrorCode)); } } } #endregion 

编辑:24.08.2012(我设法找出问题并让服务器工作)服务器的完整代码是: SERVER:

 #region FILE TRANSFER USING C#.NET SOCKET - SERVER class FTServer { IPEndPoint ipEnd_server; Socket sock_server; public FTServer() { string IpAddressString = "192.168.1.102"; ipEnd_server = new IPEndPoint(IPAddress.Parse(IpAddressString), 5656); sock_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); sock_server.Bind(ipEnd_server); } public static string receivedPath = @"C:\"; public static string curMsg_server = "Stopped!"; public void StartServer() { try { curMsg_server = "Starting..."; sock_server.Listen(100); curMsg_server = "Running and waiting to receive file."; Socket clientSock = sock_server.Accept(); clientSock.ReceiveBufferSize = 16384; byte[] clientData = new byte[1024 * 50000]; int receivedBytesLen = clientSock.Receive(clientData, clientData.Length, 0); curMsg_server = "Receiving data..."; int fileNameLen = BitConverter.ToInt32(clientData, 0); string fileName = Encoding.UTF8.GetString(clientData, 4, fileNameLen); BinaryWriter bWrite = new BinaryWriter(File.Open(receivedPath + fileName, FileMode.Append)); bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen); while (receivedBytesLen > 0) { receivedBytesLen = clientSock.Receive(clientData, clientData.Length, 0); if (receivedBytesLen == 0) { bWrite.Close(); } else { bWrite.Write(clientData, 0, receivedBytesLen); } } curMsg_server = "Saving file..."; bWrite.Close(); clientSock.Close(); curMsg_server = "Received and Archived file [" + fileName + "] (" + (receivedBytesLen - 4 - fileNameLen) + " bytes received); Server stopped."; } catch (SocketException ex) { curMsg_server = "File Receving error."; MessageBox.Show(String.Format("{0} Error code: {1}", ex.Message, ex.ErrorCode)); } } } #endregion 

你已经陷入了最经典的套接字使用陷阱。 这个与伯克利开发的sockets概念一样古老。

你看,你还没有看过文档:)

例如,请参阅http://msdn.microsoft.com/en-us/library/ms145160.aspx – 返回值是多少?

发送和接收方法都没有义务实际发送/接收您提供的所有数据。 这就是为什么他们都返回一个’int’来描述实际发送/接收的数量。 由于系统的内部缓冲区有限,因此保留了此设计。 如果你提供一个999GB的arrays要发送,你的网卡在实际发送之前将如何存储?

您看到8KB阈值的行为,可能是因为这是内部缓冲区的大小,或者可能是TCP网络数据包的最大大小。我不记得它们有多大,但它是围绕着这个。

要正确地发送和接收数据,您必须使用某种循环,例如,以简化forms:

 int bytesToBeSent = arr.Length; int bytesActuallySent = 0; while(bytesActuallySent < bytesToBeSent) bytesActuallySent += socket.Send(arr, bytesActuallySent, bytesToSend - bytesActuallySent, ....); 

和recv - 类似。

来自MSDN :

Receive方法将读取尽可能多的数据,最多可达size参数指定的字节数

不能保证一次性读取所有内容; 它会读取最大值(在您的情况下缓冲区的大小)。 从任何流中读取时,通常需要具有读/接收循环。 在你的情况下,我会说缓冲区非常大,你应该手动读取长度字节,然后在一个缓冲区(比如4096字节)上进行文件处理的读/接收循环。 您不需要512k缓冲区。

经典的读/接收循环将是:

 int read; byte[] buffer = new byte[4096]; while((read = socket.Receive(buffer)) > 0) { output.Write(buffer, 0, read); } 

在每个发送方法大约200 ms之前需要延迟,因为接收缓冲区被传入数据覆盖。 例:

 Thread.Sleep(200); socket.send(sendbuffer);