[英]File Transfer using sockets and multiple clients
我有一個使用.Net remoting編寫的大型應用程序用於文件傳輸。 在某些情況下,套接字被強行關閉是不可能的 - 我沒有直接使用套接字,而是使用字節數組的.Net遠程調用(我沒有在一次傳輸中發送整個文件,我正在拆分它)。
所以,我決定更改實際的文件傳輸部分以使用套接字。
作為一個概念證明,看看我是否正確的原則,我已經編寫了一個簡單的控制台客戶端和一個服務器。
我正在使用ASynch接收但是同步寫入 - 我已經嘗試過同時進行ASync但同樣的研究,並保持同步使調試更容易。
應用程序執行的操作(下面的代碼)是服務器所在並等待傳輸文件,並將它們存儲在給定名稱的目錄中。
按下enter鍵后,服務器會讀取收到的文件並將其發送回以不同名稱存儲它們的客戶端。 我想兩種方式測試文件傳輸。
使用客戶端應用程序的一個實例,一切都很好 - 服務器接收它然后將其發送回客戶端。 一切都很好。 是的,當您終止服務器時,客戶端會拋出異常 - 但這很好 - 我知道套接字被強制關閉......我可以處理在代碼工作時整理代碼。
但是,當我創建2個客戶端代碼實例時(不要忘記稍微修改代碼以讀取要發送的不同文件,並且還以不同的名稱存儲接收的文件) - 服務器從客戶端接收這兩個文件,發送第一個回來就好了,然后幾個段進入第二個文件它拋出“非阻塞套接字操作無法立即完成” - 這很奇怪,因為沒有阻塞,並且接收是異步的 - 並且發送是實際阻塞!
請問我做錯了什么 - 毫無疑問這是愚蠢的,但仍然......
最終代碼的目的是能夠讓n個客戶端聯系服務器並向其發送文件,並且隨機間隔讓服務器將一個或多個文件發送回一些/所有客戶端。
干杯人!
服務器代碼
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace SocketServer
{
class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
public int client;
}
class Program
{
static int chunkSize = 16 * 1024;
static int chucksizeWithoutHeaderData = chunkSize - 8;
static List<ConnectionInfo> list = new List<ConnectionInfo>();
static Socket serverSocket;
static int nClient = 0;
static void AcceptCallback(IAsyncResult result)
{
ConnectionInfo info = new ConnectionInfo();
info.Socket = serverSocket.EndAccept(result);
info.Buffer = new byte[chunkSize];
Console.WriteLine("Client connected");
nClient++;
info.client = nClient;
list.Add(info);
info.Socket.BeginReceive(info.Buffer,0,info.Buffer.Length,SocketFlags.None, new AsyncCallback(ReceiveCallBack), info);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback),null);
}
static void ReceiveCallBack(IAsyncResult result)
{
ConnectionInfo info = result.AsyncState as ConnectionInfo;
try
{
Int32 nSegmentNumber = BitConverter.ToInt32(info.Buffer,0);
Int32 nMaxSegment = BitConverter.ToInt32(info.Buffer,4);
string strFileName = string.Format(@"c:\temp\from-client-{0}.dat",info.client);
int bySize = info.Socket.EndReceive(result);
using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate))
{
Console.WriteLine("Received segment {0} of {1} from client {2}", nSegmentNumber, nMaxSegment, info.client);
fs.Position = fs.Length;
fs.Write(info.Buffer, 8, bySize-8);
if (nSegmentNumber >= nMaxSegment)
{
Console.WriteLine("Completed receipt from client {0}", info.client);
}
}
info.Socket.BeginReceive(info.Buffer, 0, info.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), info);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void Main(string[] args)
{
try
{
Console.WriteLine("Server");
IPAddress address = IPAddress.Parse("127.0.0.1"); //The IP address of the server
IPEndPoint myEndPoint = new IPEndPoint(address, 6503);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(myEndPoint);
serverSocket.Listen(1000);
for (int n = 0; n < 10; ++n)
{
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
Console.WriteLine("Server now waiting");
Console.ReadLine();
foreach (ConnectionInfo info in list)
{
string strFileName = string.Format(@"c:\temp\from-client-{0}.dat", info.client);
using (FileStream fs = new FileStream(strFileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
int nMaxChunk = 0;
int nCurrentChunk = 0;
nMaxChunk = (int)(fs.Length / chucksizeWithoutHeaderData);
if ((nMaxChunk * chucksizeWithoutHeaderData) < fs.Length)
{
++nMaxChunk;
}
using (BinaryReader br = new BinaryReader(fs))
{
byte[] byBuffer;
Int64 nAmount = 0;
byte[] byMaxChunk = BitConverter.GetBytes(nMaxChunk);
while (fs.Length > nAmount)
{
++nCurrentChunk;
byte[] byCurrentChunk = BitConverter.GetBytes(nCurrentChunk);
byBuffer = br.ReadBytes(chucksizeWithoutHeaderData);
Console.WriteLine("Sending {0}bytes, chunk {1} of {2} to client {3}", byBuffer.Length,nCurrentChunk,nMaxChunk, info.client);
byte [] byTransmitBuffer = new byte[byBuffer.Length + 8];
Array.Copy(byCurrentChunk, byTransmitBuffer, 4);
Array.Copy(byMaxChunk, 0,byTransmitBuffer, 4, 4);
Array.Copy(byBuffer, 0, byTransmitBuffer, 8, byBuffer.Length);
info.Socket.Send(byTransmitBuffer);
nAmount += byBuffer.Length;
}
}
}
}
Console.WriteLine("Press enter to end server");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
}
}
}
客戶代碼
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace SocketClient
{
class Program
{
static TcpClient socket = new TcpClient();
static int chunkSize = 16 * 1024;
static int chucksizeWithoutHeaderData = chunkSize - 8;
static byte[] byReceiveBuffer = new byte[chunkSize];
static void ReceiveCallBack(IAsyncResult result)
{
Socket socket = result.AsyncState as Socket;
try
{
int bySize = socket.EndReceive(result);
Console.WriteLine("Recieved bytes {0}", bySize);
if (bySize != 0)
{
Int32 nSegmentNumber = BitConverter.ToInt32(byReceiveBuffer, 0);
Int32 nMaxSegment = BitConverter.ToInt32(byReceiveBuffer, 4);
Console.WriteLine("Received segment {0} of {1}", nSegmentNumber, nMaxSegment);
string strFileName = string.Format(@"c:\temp\client-from-server.dat");
using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate))
{
fs.Position = fs.Length;
fs.Write(byReceiveBuffer, 8, bySize-8);
}
if (nSegmentNumber >= nMaxSegment)
{
Console.WriteLine("all done");
}
}
socket.BeginReceive(byReceiveBuffer, 0, byReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void Main(string[] args)
{
Console.WriteLine("Press enter to go");
Console.ReadLine();
socket.Connect("127.0.0.1", 6503);
Console.WriteLine("Client");
Console.ReadLine();
byte[] byBuffer;
socket.Client.BeginReceive(byReceiveBuffer, 0, byReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket.Client);
using (FileStream fs = new FileStream(@"c:\temp\filetosend.jpg", FileMode.Open, FileAccess.Read, FileShare.None))
{
using (BinaryReader br = new BinaryReader(fs))
{
int nMaxChunk = 0;
int nCurrentChunk = 0;
nMaxChunk = (int)(fs.Length / chucksizeWithoutHeaderData);
if ((nMaxChunk * chucksizeWithoutHeaderData) < fs.Length)
{
++nMaxChunk;
}
byte[] byMaxChunk = BitConverter.GetBytes(nMaxChunk);
Int64 nAmount = 0;
while (fs.Length > nAmount)
{
++nCurrentChunk;
byte[] byCurrentChunk = BitConverter.GetBytes(nCurrentChunk);
byBuffer = br.ReadBytes(chucksizeWithoutHeaderData);
Console.WriteLine("Sending {0}bytes, chunk {1} of {2}", byBuffer.Length, nCurrentChunk, nMaxChunk);
byte[] byTransmitBuffer = new byte[byBuffer.Length + 8];
Array.Copy(byCurrentChunk, byTransmitBuffer, 4);
Array.Copy(byMaxChunk, 0, byTransmitBuffer, 4, 4);
Array.Copy(byBuffer, 0, byTransmitBuffer, 8, byBuffer.Length);
socket.Client.Send(byTransmitBuffer);
nAmount += byBuffer.Length;
}
}
}
Console.WriteLine("done");
Console.ReadLine();
}
}
}
套接字編程可能很棘手。 如果發送100個字節,並不意味着您將在服務器中收到100個字節。 您可以在多個數據包中接收這100個字節,您必須添加代碼來控制它。
我說因為您的服務器代碼中的這一行:
fs.Write(info.Buffer, 8, bySize-8);
你假設你將收到至少8位,這可能是錯的。 bySize可以小於塊大小(如果連接已被客戶端關閉,則可以為零,如果出現錯誤,則為<0)。
關於您的錯誤,我測試了您的代碼,我可以復制您的問題:
服務器崩潰是因為套接字在傳輸完所有文件后正在等待更多數據。 客戶關閉了它。
我解決了在發送文件后關閉客戶端連接的問題:
socket.Client.Disconnect(false);
然后服務器接收bySize = 0字節,表示連接已關閉。 在服務器中我替換了這個:
int bySize = socket.EndReceive(result);
為了這:
int bySize = 0;
try
{
bySize = info.Socket.EndReceive(result);
}
catch (Exception ex)
{
Console.WriteLine("Error from client {0}: {1}", info.client, ex.Message);
return;
}
if (bySize <= 0)
return;
看看這里: http : //msdn.microsoft.com/en-us/library/5w7b7x5f.aspx#Y240
在這里: http : //msdn.microsoft.com/en-us/library/fx6588te.aspx
編輯:我忘記提到這一點,你只需要調用一次BeginAccept。 我刪除了for語句。
// for (int n = 0; n < 10; ++n)
// {
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
// }
我認為我可能有一個解決方案 - 雖然我不是100%自信......如果失敗,我會繼續測試並報告。
無論如何 - 我已經將socket.SendBufferSize和RecieveBufferSize設置為塊大小的4倍,現在一切似乎都很好。
那里的東西(重新緩沖大小)只是把問題關掉了,就像我想的那樣。
但是,我在其他地方遇到了一段代碼,並將這些行放入代碼中解決了問題。
try
{
bySent = info.Socket.Send(byTransmitBuffer);
}
catch (SocketException ex)
{
Console.WriteLine("Only sent {0}, remaining = {1}", bySent,fs.Length -nAmount);
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
throw ex; // any serious error occurr
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.