經過一段時間的學習,對於Socket創建C/S模型有了一些心得,寫在這裏供自己和大家參考。
一般服務器都要和多個客戶端連接通訊,當所有通訊都是TCP連接時,需要服務器和每個客戶端保持着連接,並且服務器還要很好的處理將要來的連接請求以及可能會斷開的客戶。
1 處理將要來的連接請求(支持關閉套接字):在這裏我是專開了一個線程去監聽網絡,但由於accept函數是阻塞的,所以服務器在關閉時(不再監聽網絡)就會出異常,後來我在網上找了一下,說select函數可以解決這個問題:select函數就是判斷網絡有沒有連接請求(我讓它每毫秒判斷一次),如果有則再去accept,沒有的話繼續判斷。具體說明見msdn。我的例子如下:
sListener.Listen(10);
Console.WriteLine("Start listen");
ArrayList listenList = new ArrayList();
listenList.Add(sLis:ener);
int i = 0;
while (true)
{
if (this.ifRun == false) //服務器可以通過ifRun來關閉監聽
break;
Socket.Select(listenList, null, null, 1000); //如果網絡沒有連接請求,Count==0
if (listenList.Count == 0){
listenList.Add(sListener);
Thread.Sleep(1);
continue;
}
Socket handler = sListener.Accept();
Console.WriteLine("Come one !");
Console.WriteLine(handler.RemoteEndPoint.ToString());
Console.WriteLine(handler.LocalEndPoint.ToString());
ClassRecv exm = new ClassRecv(handler); //開闢線程去和該客戶端傳輸數據
exm.saveRootPath = this.saveRootPath;
Thread t = new Thread(exm.Recv);
t.Start();
} //while(true)
//Console.WriteLine("Succeed stop");
this.sListener.Close();
2 局域網速度控制(關鍵是不限速如何確保安全傳輸)
引出問題:
轉---------開始
問:
如果在局域網內開發一文件傳輸程序(局域網網卡均爲100M),文件被分割成N個包發送的話,使用TCP直接發送,如果我發完一個包不延時的話肯定會丟包。TCP協議不是號稱能夠避免丟包嗎?如果本程序需要在外網上使用,也必須延時嗎,那這個延時值應該設置爲多少?
答:
TCP協議不是號稱能夠避免丟包嗎?
---->是保證在傳輸中不丟包。
但實際上你的問題是在應用層丟了。
TCP有滑動窗口概念,你發的包太多,對方接收緩衝滿了,你又不檢查錯誤日誌,盲目發,當然有問題,
-----
延時值應該設置爲多少?
-----》依據你的網絡情況,實際上你不要這樣亂髮,應該一發一收,收到響應再發下一包,這樣可適應任何網絡情況。
轉---------完
這是網上的人對此問題的一個解釋,我看了好多文章,覺得這個文章比較有道理。
我的問題:
我和上面那兄弟的問題差不多,但是我在接收端加了一個返回消息,照樣不行(傳的是5m的東西,後面2m就容易出錯),我還看了一個不算太標準的ftp服務器的源代碼,在不限速的情況下也沒有什麼特別處理。
我加了個方法,當接受放給發送法發送返回消息時,先用select函數判斷當前socket是否可寫----發送,如果不可就sleep(1)循環到可以寫。
結果:程序結構還不好,內存佔用太多,發送速度大約是QQ的60%多一點。
測試環境:同一個宿舍的兩臺機子。
內存佔用問題已經解決,是因爲代碼寫的不太好。 現在副上源代碼:
Server(關鍵部分)
byte[] recvBuffer = new byte[1024]; //接受文件頭信息
int byteRec = this.handler.Receive(recvBuffer);
// Console.WriteLine("Recving Protocol Struct........");
XmlSerializer structSerializer = new XmlSerializer(typeof(TransStruct));
MemoryStream stream = new MemoryStream();
//Received byte to stream
stream.Write(recvBuffer, 0, recvBuffer.Length);
stream.Position = 0; //IMP;
//Call the Deserialize method and cast to the object type
TransStruct RecvStruct = (TransStruct)structSerializer.Deserialize(stream);
//Now should recv file
String name = Path.GetFileName(RecvStruct.filepathName);
string str = this.saveRootPath + "//" + name;
FileStream fs = new FileStream(str, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
int buffersize = 4 * 1024; //網絡緩衝是8k(不知道寫得是否標準)
byte[] fileBytes = new byte[buffersize];
int i = 0;
ArrayList listenList = new ArrayList(); //////////////////////
string strT = "&&&&";
byte[] strTB = new byte[4];
while ((RecvStruct.FILESIZE - i) >= buffersize) //接收文件
{
int n =this.handler.Receive(fileBytes);
//fileBuffer.AddRange(fileBytes);
i += fileBytes.Length;
fs.Write(fileBytes, 0, fileBytes.Length);
//select
listenList.Add(this.handler);
Socket.Select(listenList, null, null, 1000);
while (listenList.Count == 0)
{
listenList.Clear();
listenList.Add(this.handler);
Socket.Select(null, listenList, null, 1000);
Thread.Sleep(1);
}
strTB = Encoding.ASCII.GetBytes(strT);
this.handler.Send(strTB);
}
if ((int)(RecvStruct.FILESIZE - i) > 0) //看文件有沒有最後一小部分沒有傳輸完成
{
byte[] lastBuffer = new byte[(int)(RecvStruct.FILESIZE - i)];
this.handler.Receive(lastBuffer);
fs.Write(lastBuffer, 0, lastBuffer.Length);
// fileBuffer.AddRange(lastBuffer);
}
//fileBytes = new byte[fileBuffer.Count];
//fileBuffer.CopyTo(fileBytes);
fs.Write(fileBytes, 0, fileBytes.Length);
fs.Flush();
fs.Close();
// Configration.FileReceived++;
handler.Shutdown(SocketShutdown.Both);
handler.Close();
Client:
沒什麼特別的,和Server相對應。
新問題:
我對於異常處理還沒有完全掌握,要作出一個穩定的系統,必須做好它。 我的傳輸速度(在不限速的情況下)大約是QQ,ftp的60%多,但是已經不出錯了。 目前主要考慮穩定性,所以速度先放一下。看過ftp客戶端源代碼,並沒有返回之類的判斷,所以速度快。關鍵問題在於ftp服務器是怎麼做的。