原文URL: http://www.cnblogs.com/dlwang2002/archive/2008/09/16/1291793.htmlhtml
1:基於Socket鏈接;web
2:在四個局域網內測試經過;數據庫
3:簡單模型,須要進一步優化效率和處理;服務器
========================= 備註: 通過修改和重構,抽象出來的P2PLib已經能夠工做了,而且提取出一個Envelope對象,看成通信協議和數據載體。 在這個Lib之上,很容易實現不一樣局域網之間的具體應用,好比如今已經創建了三個應用: 1:IM,跨局域網即時通信(UI像MSN) 2:FT,文件傳輸,斷點續傳 3:DP,數據庫代理(客戶須要在任何地方訪問它局域網內的數據庫,這一個實際上是做爲某ORM的數據層出現的。)
========================== 基本思路socket
兩個分別在不一樣的局域網內的用戶沒法直接創建鏈接並通信。由於處在不一樣局域網的用戶(沒有公共IP)沒法被外部機器主動鏈接,因此凡是所謂的P2P通常都是經過中間服務器中轉通信的。好比在幾年前俺曾經介紹過一個P2P的軟件,http://www.cnblogs.com/dlwang2002/archive/2005/04/14/207988.html,基本原理那裏面有介紹。post
此次所創建的模型,是雙方都在不一樣的局域網內部,都沒有公用IP。測試
基本原理是這樣的。局域網A內用戶PA想要和局域網B內的用戶PB通信,那麼須要經過中間服務器S進行轉接通信。Socket連接雖然只是由一方發起(局域網內的),可是socket確實一個能夠在兩端都能通信的,也就是說,PA連接S後,S實際上可使用這個通道直接發消息給PA。同理,若是PB鏈接以後,S將有兩個Socket實例,而後S能夠把SA的消息直接轉發給SB,這樣SB就轉載了SA的請求到了PB。雖然仍是要經過中轉,可是S只負責把兩端Socket互聯,速度延時可近似認爲是0,也就是能夠認爲PA和PB是創建了直接的連接,P2P。優化
過程以下:this
1:)PA向S發出鏈接請求;S接受請求,而且保留住PA的socket實例SA,存進一個在線用戶列表LiveConnections中spa
2:)PB請求S並創建鏈接(和A無前後關係),S中保存其socket實例SB
3:)PA向s發出通信請求,指明通信對象是PB
4:)S接收到A的請求,再當前的LiveConnections中找到PB的socket示例SB,轉發消息;
5:)PB接受到來自PA的消息。
主要程序代碼
1:)首先的問題是如何創建Socket鏈接。這個問題在之前的一篇Blog中有提到(http://www.cnblogs.com/dlwang2002/archive/2008/07/21/924803.html)。這裏使用的代碼基本上都是和那一個同樣的,只有中間處理通信數據的部分稍有不一樣。這些代碼再也不贅述。
2:)服務器S處理轉發消息的代碼
1
#region hander data 2
try 3
{ 4
Logger.Log("DataReceive", data);//test, catch the data transaction 5
6
string[] allData = data.Split(';'); 7
string requestTo = allData[0]; // this is the basic format: to_IP;from_IP;MSG 8
9
//find stored socket connection here10
ClientConnection requectToCC = null; 11
foreach (DictionaryEntry de in SocketListener.ClientConnections) 12
{ 13
ClientConnection cc = de.Key as ClientConnection; 14
// request form will be like this "xx.xx.xxx.xxx:xxxxxx",the last number is running number 15
// so if 2 client both in the same network, this will be confued to dispatch the socket 16
//here just find the last one that in the same network17
if (cc.RequestFrom.IndexOf(requestTo) > -1) 18
{ 19
requectToCC = cc; 20
}21
22
}23
if (requectToCC != null && requectToCC.ConSocket.Connected) 24
{ 25
//can get the connection here, then transfer this request to it26
try27
{ 28
ReplayMsg(requectToCC.ConSocket, data); 29
}30
catch (Exception ce) 31
{ 32
ReplayMsg(this.ConSocket, "process error: " + ce.Message); 33
Logger.Log("SendMsgError", ce.Message); 34
}35
}36
else37
{ 38
//if can not find, means the reqeust to is not login/register there39
ReplayMsg(this.ConSocket, "the client you request to is disconnected"); 40
Logger.Log("SendMsgError", "the client you request to is disconnected"); 41
}42
// 43
}44
catch (Exception ex) 45
{ 46
Logger.Log("DataError " + _requestFrom, ex.Message); 47
}48
#endregion
3:)客戶端的簡單實現
1 namespace P2PClient 2 { 3 public class ConnectionManager 4 { 5 private Socket _socket; 6 private IPEndPoint _hostEP; 7 private string _localIP; 8 9 public delegate void MessageReceiveEvent(string fromIP,string msg); 10 11 public event MessageReceiveEvent OnMsgReceived; 12 13 public ConnectionManager(string ip, int port) 14 { 15 IPAddress address = IPAddress.Parse(ip); 16 _hostEP = new IPEndPoint(address, port); 17 } 18 19 public bool Connect2Server() 20 { 21 try22 { 23 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 24 _socket.Connect(_hostEP); 25 26 IPHostEntry oIPHost = Dns.Resolve(Environment.MachineName); 27 _localIP = oIPHost.AddressList[0].ToString(); 28 29 //there use the same socket, to wait for reply msg30 Thread thread = new Thread(new ThreadStart(WaitForReceiveData)); 31 thread.Name = "connection_"; 32 thread.Start(); 33 34 return true; 35 } 36 catch (Exception ex) 37 { 38 return false; 39 } 40 } 41 public void Close() 42 { 43 _socket.Shutdown(SocketShutdown.Both); 44 _socket.Close(); 45 } 46 public void SendMessage(string clientIP, string msg) 47 { 48 string sendStr = clientIP + ";"+_localIP+";" + msg;//simple format; to_IP;from_IP;MSG49 50 byte[] bytesSendStr = new byte[1024]; 51 bytesSendStr = Encoding.ASCII.GetBytes(sendStr); 52 _socket.Send(bytesSendStr, bytesSendStr.Length, 0); 53 } 54 55 public void WaitForReceiveData() 56 { 57 byte[] bytes = new Byte[1024]; 58 59 while (true) 60 { 61 bytes = new byte[1024]; 62 string data = ""; 63 64 //the system while be wait here until there is msg received here65 int bytesRec = this._socket.Receive(bytes); 66 67 data += Encoding.ASCII.GetString(bytes, 0, bytesRec); 68 #region hander data69 Logger.Log("DataReceive", data); 70 71 string[] allData = data.Split(';'); 72 string requestFrom = allData[1]; // this is the basic format; to_IP;from_IP;MSG73 string msg=allData[2]; 74 75 if (OnMsgReceived != null) 76 { 77 OnMsgReceived(requestFrom, msg); 78 } 79 #endregion80 } 81 } 82 83 84 }
4
:)UI
等其餘處理
(略)
問題
1:)一個Socket的實例能夠在服務器/客戶端存活多久呢?我測試發現,至少幾個小時沒有問題,可是最長時間殊不知道。
2:)服務器S用單獨的線程來處理連接,並非最好的方式
3:)服務器負載平衡,在多個服務器的狀況下,要讓客戶端能夠選擇效率最高的服務器進行中轉
4:)有一臺機器已經在公網上,或者兩臺都在公網上,須要另外的模型。他們不須要中轉。
小結
簡單,效率未知。
