Socket一般用于网络之间的通信,在这里,实现的是服务端与客户端的简单消息通信。
首先是客户端的搭建,一般步骤是先建立Socket绑定本地的IP和端口,并对远端连接进行连接进行监听,这里的监听一般开启后台线程进行循环处理;如果远端有连接到本机的Socket的端口,则获取一个新的Socket对象并重新添加一个线程用于对远端地址进行消息通信(消息的收发),这样,服务端的Socket就简单实现,下面是winForm的具体实现。
一、先建立Socket的服务类SocketServerManager,用于对Socket各种操作的统一管理:
1 public class SocketManager 2 { 3 Socket _socket = null; 4 EndPoint _endPoint = null; 5 bool _isListening = false; 6 int BACKLOG = 10; 7 8 public SocketManager(string ip, int port) 9 { 10 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 11 IPAddress _ip = IPAddress.Parse(ip); 12 _endPoint = new IPEndPoint(_ip, port); 13 } 14 15 public void Start() 16 { 17 _socket.Bind(_endPoint); //绑定端口 18 _socket.Listen(BACKLOG); //开启监听 19 Thread acceptServer = new Thread(AcceptWork); //开启新线程处理监听 20 acceptServer.IsBackground = true; 21 _isListening = true; 22 acceptServer.Start(); 23 } 24 25 public void AcceptWork() 26 { 27 while (_isListening) 28 { 29 Socket acceptSocket = _socket.Accept(); 30 if (acceptSocket != null) 31 { 32 Thread socketConnectedThread = new Thread(newSocketReceive); 33 socketConnectedThread.IsBackground = true; 34 socketConnectedThread.Start(acceptSocket); 35 } 36 Thread.Sleep(200); 37 } 38 } 39 40 public void newSocketReceive(object obj) 41 { 42 Socket socket = obj as Socket; 43 while (true) 44 { 45 try 46 { 47 if (socket == null) return; 48 //这里向系统投递一个接收信息的请求,并为其指定ReceiveCallBack做为回调函数 49 socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallBack, buffer); 50 } 51 catch (Exception ex) 52 { 53 return; 54 } 55 Thread.Sleep(100); 56 } 57 } 58 59 private void ReceiveCallBack(IAsyncResult ar) 60 { 61 } 62 }
上面是Socket管理类的模型,具体的方法是初始化和开启监听,接下来就是在Form的界面调用建立类和Start方法。
客户端同样是初始化socket,然后就不是监听socket,而是调用Connect连接指定的Socket地址,最后是开启新的线程接收和发送消息。
1 public class SocketClientManager 2 { 3 public Socket _socket = null; 4 public EndPoint endPoint = null; 5 public bool _isConnected = false; 6 7 public SocketClientManager(string ip, int port) 8 { 9 IPAddress _ip = IPAddress.Parse(ip); 10 endPoint = new IPEndPoint(_ip, port); 11 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 12 } 13 14 public void Start() 15 { 16 _socket.BeginConnect(endPoint, ConnectedCallback, _socket); 17 _isConnected = true; 18 Thread socketClient = new Thread(SocketClientReceive); 19 socketClient.IsBackground = true; 20 socketClient.Start(); 21 } 22 23 public void SocketClientReceive() 24 { 25 while (_isConnected) 26 { 27 try { 28 _socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, buffer); 29 } 30 catch (SocketException ex) 31 { 32 _isConnected = false; 33 } 34 35 Thread.Sleep(100); 36 } 37 } 38 39 public void ReceiveCallback(IAsyncResult ar) 40 { 41 } 42 }
主要记住的是,客户端是监听Socket是固定的,是监听绑定地址的,每当有新的连接访问,则开启新的线程与之进行交互,而客户端只简单实现与服务端交互,服务端则只有一个。
Socket的进行发送与接收,一般是通过异步方法BeginReceive和BeginSend进行处理,方法一般带有回调函数,用于执行操作之后的处理。
还有就是连接的关闭,每关闭一个连接,先要结束在Socket所在的线程方法,我这里的处理是停止掉死循环的函数调用,每当线程所在函数执行完毕,则线程自动销毁。之后就是关闭所连接的socket。
下面是我程序的完整实现,为了方便socket的管理,我把服务器的所有与客户端对话的Socket统一用字典管理,并封装在SocketInfo的内部类中,消息的发送与接收必须先找到该连接socket。
最后就是界面的调用,完成Socket的网络消息交互。
下面是具体的实现及源码:
1 public class SocketManager 2 { 3 public Dictionary<string,SocketInfo> _listSocketInfo = null; 4 Socket _socket = null; 5 public SocketInfo socketInfo = null; 6 EndPoint _endPoint = null; 7 bool _isListening = false; 8 int BACKLOG = 10; 9 10 public delegate void OnConnectedHandler(string clientIP); 11 public event OnConnectedHandler OnConnected; 12 public delegate void OnReceiveMsgHandler(string ip); 13 public event OnReceiveMsgHandler OnReceiveMsg; 14 public event OnReceiveMsgHandler OnDisConnected; 15 16 public SocketManager(string ip, int port) 17 { 18 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 19 IPAddress _ip = IPAddress.Parse(ip); 20 _endPoint = new IPEndPoint(_ip, port); 21 _listSocketInfo = new Dictionary<string, SocketInfo>(); 22 } 23 24 public void Start() 25 { 26 _socket.Bind(_endPoint); //绑定端口 27 _socket.Listen(BACKLOG); //开启监听 28 Thread acceptServer = new Thread(AcceptWork); //开启新线程处理监听 29 acceptServer.IsBackground = true; 30 _isListening = true; 31 acceptServer.Start(); 32 } 33 34 public void AcceptWork() 35 { 36 while (_isListening) 37 { 38 Socket acceptSocket = _socket.Accept(); 39 if (acceptSocket != null && this.OnConnected != null) 40 { 41 SocketInfo sInfo = new SocketInfo(); 42 sInfo.socket = acceptSocket; 43 _listSocketInfo.Add(acceptSocket.RemoteEndPoint.ToString(), sInfo); 44 OnConnected(acceptSocket.RemoteEndPoint.ToString()); 45 Thread socketConnectedThread = new Thread(newSocketReceive); 46 socketConnectedThread.IsBackground = true; 47 socketConnectedThread.Start(acceptSocket); 48 } 49 Thread.Sleep(200); 50 } 51 } 52 53 public void newSocketReceive(object obj) 54 { 55 Socket socket = obj as Socket; 56 SocketInfo sInfo = _listSocketInfo[socket.RemoteEndPoint.ToString()]; 57 sInfo.isConnected = true; 58 while (sInfo.isConnected) 59 { 60 try 61 { 62 if (sInfo.socket == null) return; 63 //这里向系统投递一个接收信息的请求,并为其指定ReceiveCallBack做为回调函数 64 sInfo.socket.BeginReceive(sInfo.buffer, 0, sInfo.buffer.Length, SocketFlags.None, ReceiveCallBack, sInfo.socket.RemoteEndPoint); 65 } 66 catch (Exception ex) 67 { 68 return; 69 } 70 Thread.Sleep(100); 71 } 72 } 73 74 private void ReceiveCallBack(IAsyncResult ar) 75 { 76 EndPoint ep = ar.AsyncState as IPEndPoint; 77 SocketInfo info = _listSocketInfo[ep.ToString()]; 78 int readCount = 0; 79 try 80 { 81 if (info.socket == null) return; 82 readCount = info.socket.EndReceive(ar); 83 }catch(Exception ex){ 84 return; 85 } 86 if (readCount > 0) 87 { 88 //byte[] buffer = new byte[readCount]; 89 //Buffer.BlockCopy(info.buffer, 0, buffer, 0, readCount); 90 if (readCount < info.buffer.Length) 91 { 92 byte[] newBuffer = new byte[readCount]; 93 Buffer.BlockCopy(info.buffer, 0, newBuffer, 0, readCount); 94 info.msgBuffer = newBuffer; 95 } 96 else 97 { 98 info.msgBuffer = info.buffer; 99 } 100 string msgTip = Encoding.ASCII.GetString(info.msgBuffer); 101 if (msgTip == "