Socket一般用於網絡之間的通信,在這裡,實現的是服務端與客戶端的簡單消息通信。
首先是客戶端的搭建,一般步驟是先建立Socket綁定本地的IP和端口,並對遠端連接進行連接進行監聽,這裡的監聽一般開啟後台線程進行循環處理;如果遠端有連接到本機的Socket的端口,則獲取一個新的Socket對象並重新添加一個線程用於對遠端地址進行消息通信(消息的收發),這樣,服務端的Socket就簡單實現,下面是winForm的具體實現。
一、先建立Socket的服務類SocketServerManager,用於對Socket各種操作的統一管理:
public class SocketManager
{
Socket _socket = null;
EndPoint _endPoint = null;
bool _isListening = false;
int BACKLOG = 10;
public SocketManager(string ip, int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress _ip = IPAddress.Parse(ip);
_endPoint = new IPEndPoint(_ip, port);
}
public void Start()
{
_socket.Bind(_endPoint); //綁定端口
_socket.Listen(BACKLOG); //開啟監聽
Thread acceptServer = new Thread(AcceptWork); //開啟新線程處理監聽
acceptServer.IsBackground = true;
_isListening = true;
acceptServer.Start();
}
public void AcceptWork()
{
while (_isListening)
{
Socket acceptSocket = _socket.Accept();
if (acceptSocket != null)
{
Thread socketConnectedThread = new Thread(newSocketReceive);
socketConnectedThread.IsBackground = true;
socketConnectedThread.Start(acceptSocket);
}
Thread.Sleep(200);
}
}
public void newSocketReceive(object obj)
{
Socket socket = obj as Socket;
while (true)
{
try
{
if (socket == null) return;
//這裡向系統投遞一個接收信息的請求,並為其指定ReceiveCallBack做為回調函數
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallBack, buffer);
}
catch (Exception ex)
{
return;
}
Thread.Sleep(100);
}
}
private void ReceiveCallBack(IAsyncResult ar)
{
}
}
public class SockeServertManager
上面是Socket管理類的模型,具體的方法是初始化和開啟監聽,接下來就是在Form的界面調用建立類和Start方法。
客戶端同樣是初始化socket,然後就不是監聽socket,而是調用Connect連接指定的Socket地址,最後是開啟新的線程接收和發送消息。
public class SocketClientManager
{
public Socket _socket = null;
public EndPoint endPoint = null;
public bool _isConnected = false;
public SocketClientManager(string ip, int port)
{
IPAddress _ip = IPAddress.Parse(ip);
endPoint = new IPEndPoint(_ip, port);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public void Start()
{
_socket.BeginConnect(endPoint, ConnectedCallback, _socket);
_isConnected = true;
Thread socketClient = new Thread(SocketClientReceive);
socketClient.IsBackground = true;
socketClient.Start();
}
public void SocketClientReceive()
{
while (_isConnected)
{
try {
_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, buffer);
}
catch (SocketException ex)
{
_isConnected = false;
}
Thread.Sleep(100);
}
}
public void ReceiveCallback(IAsyncResult ar)
{
}
}
public class SocketClientManager
主要記住的是,客戶端是監聽Socket是固定的,是監聽綁定地址的,每當有新的連接訪問,則開啟新的線程與之進行交互,而客戶端只簡單實現與服務端交互,服務端則只有一個。
Socket的進行發送與接收,一般是通過異步方法BeginReceive和BeginSend進行處理,方法一般帶有回調函數,用於執行操作之後的處理。
還有就是連接的關閉,每關閉一個連接,先要結束在Socket所在的線程方法,我這裡的處理是停止掉死循環的函數調用,每當線程所在函數執行完畢,則線程自動銷毀。之後就是關閉所連接的socket。
下面是我程序的完整實現,為了方便socket的管理,我把服務器的所有與客戶端對話的Socket統一用字典管理,並封裝在SocketInfo的內部類中,消息的發送與接收必須先找到該連接socket。
最後就是界面的調用,完成Socket的網絡消息交互。
下面是具體的實現及源碼:
public class SocketManager
{
public Dictionary<string,SocketInfo> _listSocketInfo = null;
Socket _socket = null;
public SocketInfo socketInfo = null;
EndPoint _endPoint = null;
bool _isListening = false;
int BACKLOG = 10;
public delegate void OnConnectedHandler(string clientIP);
public event OnConnectedHandler OnConnected;
public delegate void OnReceiveMsgHandler(string ip);
public event OnReceiveMsgHandler OnReceiveMsg;
public event OnReceiveMsgHandler OnDisConnected;
public SocketManager(string ip, int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress _ip = IPAddress.Parse(ip);
_endPoint = new IPEndPoint(_ip, port);
_listSocketInfo = new Dictionary<string, SocketInfo>();
}
public void Start()
{
_socket.Bind(_endPoint); //綁定端口
_socket.Listen(BACKLOG); //開啟監聽
Thread acceptServer = new Thread(AcceptWork); //開啟新線程處理監聽
acceptServer.IsBackground = true;
_isListening = true;
acceptServer.Start();
}
public void AcceptWork()
{
while (_isListening)
{
Socket acceptSocket = _socket.Accept();
if (acceptSocket != null && this.OnConnected != null)
{
SocketInfo sInfo = new SocketInfo();
sInfo.socket = acceptSocket;
_listSocketInfo.Add(acceptSocket.RemoteEndPoint.ToString(), sInfo);
OnConnected(acceptSocket.RemoteEndPoint.ToString());
Thread socketConnectedThread = new Thread(newSocketReceive);
socketConnectedThread.IsBackground = true;
socketConnectedThread.Start(acceptSocket);
}
Thread.Sleep(200);
}
}
public void newSocketReceive(object obj)
{
Socket socket = obj as Socket;
SocketInfo sInfo = _listSocketInfo[socket.RemoteEndPoint.ToString()];
sInfo.isConnected = true;
while (sInfo.isConnected)
{
try
{
if (sInfo.socket == null) return;
//這裡向系統投遞一個接收信息的請求,並為其指定ReceiveCallBack做為回調函數
sInfo.socket.BeginReceive(sInfo.buffer, 0, sInfo.buffer.Length, SocketFlags.None, ReceiveCallBack, sInfo.socket.RemoteEndPoint);
}
catch (Exception ex)
{
return;
}
Thread.Sleep(100);
}
}
private void ReceiveCallBack(IAsyncResult ar)
{
EndPoint ep = ar.AsyncState as IPEndPoint;
SocketInfo info = _listSocketInfo[ep.ToString()];
int readCount = 0;
try
{
if (info.socket == null) return;
readCount = info.socket.EndReceive(ar);
}catch(Exception ex){
return;
}
if (readCount > 0)
{
//byte[] buffer = new byte[readCount];
//Buffer.BlockCopy(info.buffer, 0, buffer, 0, readCount);
if (readCount < info.buffer.Length)
{
byte[] newBuffer = new byte[readCount];
Buffer.BlockCopy(info.buffer, 0, newBuffer, 0, readCount);
info.msgBuffer = newBuffer;
}
else
{
info.msgBuffer = info.buffer;
}
string msgTip = Encoding.ASCII.GetString(info.msgBuffer);
if (msgTip == "\0\0\0faild")
{
info.isConnected = false;
if (this.OnDisConnected != null) OnDisConnected(info.socket.RemoteEndPoint.ToString());
_listSocketInfo.Remove(info.socket.RemoteEndPoint.ToString());
info.socket.Close();
return;
}
if (OnReceiveMsg != null) OnReceiveMsg(info.socket.RemoteEndPoint.ToString());
}
}
public void SendMsg(string text, string endPoint)
{
if (_listSocketInfo.Keys.Contains(endPoint) && _listSocketInfo[endPoint] != null)
{
_listSocketInfo[endPoint].socket.Send(Encoding.ASCII.GetBytes(text));
}
}
public void Stop()
{
_isListening = false;
foreach (SocketInfo s in _listSocketInfo.Values)
{
s.socket.Close();
}
}
public class SocketInfo
{
public Socket socket = null;
public byte[] buffer = null;
public byte[] msgBuffer = null;
public bool isConnected = false;
public SocketInfo()
{
buffer = new byte[1024 * 4];
}
}
}
public class SocketServerManager
public class SocketClientManager
{
public Socket _socket = null;
public EndPoint endPoint = null;
public SocketInfo socketInfo = null;
public bool _isConnected = false;
public delegate void OnConnectedHandler();
public event OnConnectedHandler OnConnected;
public event OnConnectedHandler OnFaildConnect;
public delegate void OnReceiveMsgHandler();
public event OnReceiveMsgHandler OnReceiveMsg;
public SocketClientManager(string ip, int port)
{
IPAddress _ip = IPAddress.Parse(ip);
endPoint = new IPEndPoint(_ip, port);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public void Start()
{
_socket.BeginConnect(endPoint, ConnectedCallback, _socket);
_isConnected = true;
Thread socketClient = new Thread(SocketClientReceive);
socketClient.IsBackground = true;
socketClient.Start();
}
public void SocketClientReceive()
{
while (_isConnected)
{
SocketInfo info = new SocketInfo();
try {
_socket.BeginReceive(info.buffer, 0, info.buffer.Length, SocketFlags.None, ReceiveCallback, info);
}
catch (SocketException ex)
{
_isConnected = false;
}
Thread.Sleep(100);
}
}
public void ReceiveCallback(IAsyncResult ar)
{
socketInfo = ar.AsyncState as SocketInfo;
if (this.OnReceiveMsg != null) OnReceiveMsg();
}
public void ConnectedCallback(IAsyncResult ar)
{
Socket socket = ar.AsyncState as Socket;
if (socket.Connected)
{
if (this.OnConnected != null) OnConnected();
}
else
{
if (this.OnFaildConnect != null) OnFaildConnect();
}
}
public void SendMsg(string msg)
{
byte[] buffer = Encoding.ASCII.GetBytes(msg);
_socket.Send(buffer);
}
public class SocketInfo
{
public Socket socket = null;
public byte[] buffer = null;
public SocketInfo()
{
buffer = new byte[1024 * 4];
}
}
}
public class SocketClientManager
具體源碼(.net4.5,vs2013)下載:
------------------------------------------分割線------------------------------------------
免費下載地址在 http://linux.linuxidc.com/
用戶名與密碼都是www.linuxidc.com
具體下載目錄在 /2015年資料/1月/29日/C#的Socket簡單實現消息發送/
下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm
------------------------------------------分割線------------------------------------------