[英]File transfer using sockets (C#) - received file doesn't contain full data
我創建了一個服務器和一個客戶端,用於使用套接字連接進行文件傳輸。 我面臨的問題是,如果接收的文件大小超過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(我設法找出問題並使服務器正常工作)服務器的完整代碼是: 服務器:
#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
您已陷入套接字使用的最經典陷阱。 這與Berkely開發的插座的想法一樣古老。
你看,你還沒有看過文檔:)
例如,請參見http://msdn.microsoft.com/zh-cn/library/ms145160.aspx-返回值是多少?
Send和Receive方法都沒有義務實際發送/接收您提供的所有數據。 這就是為什么它們都返回一個“ int”來描述實際發送/接收的數量的原因。 之所以保留這種設計,是因為系統的內部緩沖區受到限制。 如果您提供要發送的999GB陣列,那么網卡在實際發送之前將如何存儲?
您會看到8KB閾值的行為,可能是因為這是內部緩沖區的大小,或者可能是TCP網絡數據包的最大大小。。我不記得它們有多大,但是大約是這樣。
要流行地發送和接收數據,您必須使用某種循環,例如以簡單形式:
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毫秒的延遲,因為接收緩沖區會被傳入數據覆蓋。 例:
Thread.Sleep(200);
socket.send(sendbuffer);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.