本文服務端客戶端封裝代碼轉自https://blog.csdn.net/zhujunxxxxx/article/details/44258719,並做了簡單的修改。node
1)服務端服務器
此類主要處理服務端相關消息網絡
2)客戶端app
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; namespace TcpSocketClient { public class TCPClientAsync { #region 字段 private Socket clientSock; //private byte[] recvBuffer; private ClientInfo state; #endregion #region 屬性 public IPAddress Address { get; private set; }// 監聽的IP地址 public int Port { get; private set; }// 監聽的端口 public bool IsConnected { get;private set; } #endregion #region 構造方法 public TCPClientAsync(IPAddress ipAddress,int port) { this.Address = ipAddress; this.Port = port; clientSock=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } public TCPClientAsync(IPEndPoint ipEndPort) :this(ipEndPort.Address,ipEndPort.Port) { } #endregion #region Method public void Connect()//開始鏈接 { if(!IsConnected) { //clientSock.BeginConnect(Address, Port, new AsyncCallback(HandleConnectAccepted), clientSock); clientSock.BeginConnect(Address, Port, iAsyncResult=> { try { clientSock.EndConnect(iAsyncResult); IsConnected = true; state = new TcpSocketClient.ClientInfo(clientSock); RaiseConnectAccepted(state);//掛起鏈接事件 } catch { //TODO鏈接失敗 RaiseConnectFailed(); } }, null); } } //private void HandleConnectAccepted(IAsyncResult iAsyncResult) //{ // clientSock.EndConnect(iAsyncResult); // IsConnected = true; //} public void Send(byte[] data) { if (!IsConnected) return; //clientSock.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(HandleDataSend), null); clientSock.BeginSend(data, 0, data.Length, SocketFlags.None, iAsyncResult=> { clientSock.EndSend(iAsyncResult); RaiseDataSend(state); }, null); } public void Receive() { if (!IsConnected) return; state.RecvDataBuffer = new byte[clientSock.ReceiveBufferSize]; clientSock.BeginReceive(state.RecvDataBuffer, 0, state.RecvDataBuffer.Length, SocketFlags.None, new AsyncCallback(HandleDataReceive), null); } private void HandleDataReceive(IAsyncResult iAsyncResult) { int recv = 0; try { recv = clientSock.EndReceive(iAsyncResult); if (recv == 0)//客戶端主動斷開鏈接時recv=0 { Close(); RaiseServerCloseLink(); return; } state._recvdBufferLength = recv; RaiseDataReceived(state); } catch(Exception ex) { //TODO 信息獲取失敗 string str = ex.Message; return; } //if (!IsConnected) return; clientSock.BeginReceive(state.RecvDataBuffer, 0, state.RecvDataBuffer.Length, SocketFlags.None, new AsyncCallback(HandleDataReceive), null); } //private void HandleDataSend(IAsyncResult iAsyncResult) //{ // clientSock.EndSend(iAsyncResult); //} public void Close() { //關閉數據的接受和發送 clientSock.Shutdown(SocketShutdown.Both); //清理資源 clientSock.Close(); state = null; IsConnected = false; GC.Collect(); } #endregion #region 事件 public event EventHandler<TCPMessageArgs> ConnectAccepted;//鏈接事件 public event EventHandler<TCPMessageArgs> DataSend;//數據發送事件 public event EventHandler<TCPMessageArgs> DataReceived;//數據發送事件 public event EventHandler<TCPMessageArgs> ErrorException;//錯誤信息 public event EventHandler<TCPMessageArgs> ServerCloseLink;//服務端關閉鏈接 public event EventHandler<TCPMessageArgs> ConnectFailed;//鏈接失敗 private void RaiseConnectAccepted(ClientInfo state) { if (ConnectAccepted != null) { ConnectAccepted(this, new TCPMessageArgs(state)); } } private void RaiseDataSend(ClientInfo state) { if (DataSend != null) { DataSend(this, new TcpSocketClient.TCPMessageArgs(state)); } } private void RaiseDataReceived(ClientInfo state) { if(DataReceived!=null) { DataReceived(this, new TcpSocketClient.TCPMessageArgs(state)); } } private void RaiseErrorException() { if(ErrorException!=null) { ErrorException(this, null); } } private void RaiseServerCloseLink() { if(ServerCloseLink!=null) { ServerCloseLink(this, null); } } private void RaiseConnectFailed() { if(ConnectFailed!=null) { ConnectFailed(this,null); } } #endregion } }
3)消息類異步
此類存儲相關消息socket
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TcpSocketServer { /// <summary> /// Socket TCP事件參數 /// </summary> public class TCPMessageArgs : EventArgs { /// <summary> /// 提示信息 /// </summary> public string _msg; /// <summary> /// 客戶端狀態封裝類 /// </summary> public ClientInfo state; /// <summary> /// 是否已經處理過了 /// </summary> // public bool IsHandled { get; set; } public TCPMessageArgs(string msg) { this._msg = msg; //IsHandled = false; } public TCPMessageArgs(ClientInfo state) { this.state = state; // IsHandled = false; } public TCPMessageArgs(string msg, ClientInfo state) { this._msg = msg; this.state = state; //IsHandled = false; } } }
4)服務端存儲全部客戶端類tcp
經過ide
using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Xml; namespace TcpSocketServer { /// <summary> /// 客戶端信息 /// </summary> public class ClientInfo { #region 字段 public int _recvdBufferLength;//接收到的數據長度 private byte[] _recvBuffer;// 接收數據緩衝區 private string _datagram;// 客戶端發送到服務器的報文 private Socket _clientSock;// 客戶端的Socket private string tempMsg = string.Empty; #endregion #region 屬性 /// <summary> /// 接收數據緩衝區 /// </summary> public byte[] RecvDataBuffer { get { return _recvBuffer; } set { _recvBuffer = value; } } /// <summary> /// 存取會話的報文 /// </summary> public string Datagram { get { _datagram = Encoding.Default.GetString(_recvBuffer, 0, _recvdBufferLength); return _datagram; } } public List<MsgProtocol> MsgList { get { _datagram = Encoding.Default.GetString(_recvBuffer, 0, _recvdBufferLength); return HandlerRecvdString(_datagram); } } /// <summary> /// 得到與客戶端會話關聯的Socket對象 /// </summary> public Socket ClientSocket { get { return _clientSock; } } #endregion public ClientInfo(Socket clientSocket) { _clientSock = clientSocket; } public void Close() { //關閉數據的接受和發送 _clientSock.Shutdown(SocketShutdown.Both); //清理資源 _clientSock.Close(); } public List<MsgProtocol> HandlerRecvdString(string msg) { List<MsgProtocol> msgProList = new List<MsgProtocol>(); if (!String.IsNullOrEmpty(tempMsg)) { msg = tempMsg + msg; } string pattern = "(^<protocol>.*?</protocol>)"; if (Regex.IsMatch(msg, pattern)) { //匹配協議內容 string message = Regex.Match(msg, pattern).Groups[0].Value; //將匹配的內容添加到集合 msgProList.Add(HandleMsg(message)); tempMsg = string.Empty; //截取未匹配字符串,進行下一次匹配 msg = msg.Substring(message.Length); if (!String.IsNullOrEmpty(msg)) { msgProList.AddRange(HandlerRecvdString(msg)); } } else { tempMsg = msg; } return msgProList; } private MsgProtocol HandleMsg(string msg) { MsgProtocol msgProtocol = new TcpSocketServer.MsgProtocol(); XmlDocument xml = new XmlDocument(); xml.LoadXml(msg); XmlNode node = xml.DocumentElement; XmlNode message = node.FirstChild;// xml.SelectSingleNode("message"); if(message.Attributes["type"].Value==MsgType.TEXT.ToString()) { TextProtocol text = new TcpSocketServer.TextProtocol(message.Attributes["content"].Value); msgProtocol.type = MsgType.TEXT; msgProtocol.textProtocol = text; } else { FileProtocol file = new TcpSocketServer.FileProtocol(message.Attributes["content"].Value, message.Attributes["file"].Value); msgProtocol.type = MsgType.FILE; msgProtocol.fileProtocol = file; } return msgProtocol; } } }
5)消息協議函數
處理每次消息發送時粘包問題測試
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace TcpSocketServer { public enum MsgType { UNKNOWN = 0, TEXT = 1, FILE = 2 } public class TextProtocol { public string content; private MsgType type=MsgType.TEXT; public TextProtocol(string content) { this.content = content; } //public byte[] ToBytes() //{ // string msgPackage = ToString(); // return Encoding.Default.GetBytes(msgPackage); //} public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("<message "); sb.Append(string.Format("type=\"{0}\"", type.ToString())); sb.Append(string.Format(" content=\"{0}\"", content)); sb.Append("/>"); return sb.ToString(); } } public class FileProtocol { public string filePath; public string content; private string fileName; private MsgType type = MsgType.FILE; public FileProtocol(string content,string filePath) { this.filePath = filePath; this.content = content; } public FileProtocol(string filePath) { this.filePath = filePath; } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("<message "); sb.Append(string.Format("type=\"{0}\"", type.ToString())); sb.Append(string.Format(" file=\"{0}\"", GetFileName())); sb.Append(string.Format(" content=\"{0}\"", GetFileContent())); sb.Append("/>"); return sb.ToString(); } public byte[] ToBytes() { return Encoding.Default.GetBytes(content); } private string GetFileName() { if(File.Exists(filePath)) { return Path.GetFileName(filePath); } else { return ""; } } private string GetFileContent() { if(File.Exists(filePath)) { byte[] bytes = File.ReadAllBytes(filePath); content = Encoding.Default.GetString(bytes); } return content; } } public class MsgProtocol { //公共字段 //public string fileName; public MsgType type = MsgType.UNKNOWN; public TextProtocol textProtocol; public FileProtocol fileProtocol; public MsgProtocol() { } public MsgProtocol(TextProtocol textProtocol) { this.type = MsgType.TEXT; this.textProtocol = textProtocol; } public MsgProtocol(FileProtocol fileProtocol) { this.type = MsgType.FILE; this.fileProtocol = fileProtocol; } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("<protocol>"); if(type==MsgType.TEXT) { sb.Append(textProtocol.ToString()); } else { sb.Append(fileProtocol.ToString()); } sb.Append("</protocol>"); return sb.ToString(); } public byte[] ToBytes() { return Encoding.Default.GetBytes(this.ToString()); } //public MsgProtocol(string filePath,MsgType type) //{ // type = MsgType.FILE; // this.fileName = fileName; // this.file //} } }
6)主程序應用
一、服務端
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TcpSocketServer { class Program { private static TCPServerAsync tcpServer; static void Main(string[] args) { tcpServer = new TcpSocketServer.TCPServerAsync(8899); tcpServer.DataReceived += new EventHandler<TcpSocketServer.TCPMessageArgs>(OnDataReceived); tcpServer.ClientConnected += new EventHandler<TcpSocketServer.TCPMessageArgs>(OnClientConnected); tcpServer.ClientDisconnected += new EventHandler<TcpSocketServer.TCPMessageArgs>(OnClientDisconnected); tcpServer.Start(); Console.ReadKey(); } public static void OnDataReceived(object obj,TCPMessageArgs message) { ClientInfo state = message.state; string sendData = "Server "; //byte[] data = state.RecvDataBuffer; //string strData = Encoding.Default.GetString(data); //Console.WriteLine(message.state.Datagram); #region 協議功能測試 List<MsgProtocol> msgList = state.MsgList; foreach (MsgProtocol msgProtocol in msgList) { if (msgProtocol.type == MsgType.TEXT) { Console.WriteLine(msgProtocol.textProtocol.content); sendData += msgProtocol.textProtocol.content; } else { Console.WriteLine(msgProtocol.fileProtocol.filePath); sendData += msgProtocol.fileProtocol.filePath; } } #endregion if (message.state.Datagram=="close") { tcpServer.Close(message.state); return; } //數據處理後發送回客戶端 //string sendData = "Server " + message.state.Datagram; //byte[] sendBytes = Encoding.Default.GetBytes(sendData); //tcpServer.Send(state, sendBytes); #region 協議功能測試 MsgProtocol protocol = new TcpSocketServer.MsgProtocol(new TcpSocketServer.TextProtocol(sendData)); tcpServer.Send(state, protocol.ToBytes()); #endregion } public static void OnClientConnected(object obj, TCPMessageArgs message) { Console.WriteLine("Connect:" + message.state.ClientSocket.RemoteEndPoint.ToString()); } public static void OnClientDisconnected(object obj, TCPMessageArgs message) { Console.WriteLine("Disconnect:" + message.state.ClientSocket.RemoteEndPoint.ToString()); tcpServer.Close(message.state); } } }
二、客戶端調用
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; namespace TcpSocketClient { class Program { private static TCPClientAsync tcpclient; private static bool b_Connecting = true; static void Main(string[] args) { IPAddress ip = IPAddress.Parse("192.168.8.15"); tcpclient = new TcpSocketClient.TCPClientAsync(ip, 8899); tcpclient.ConnectAccepted += new EventHandler<TcpSocketClient.TCPMessageArgs>(OnConnected); tcpclient.DataReceived += new EventHandler<TcpSocketClient.TCPMessageArgs>(OnReceived); tcpclient.ServerCloseLink += new EventHandler<TCPMessageArgs>(OnDisConnected); tcpclient.Connect(); while(b_Connecting) { string msg = Console.ReadLine(); if(msg=="exit") { tcpclient.Close(); //b_Connecting = false; break; } if(msg=="close") { //tcpclient.IsConnected = false; } //byte[] data = Encoding.Default.GetBytes(msg); //tcpclient.Send(data); //tcpclient.Receive(); MsgProtocol msgProtocol = new TcpSocketClient.MsgProtocol(new TcpSocketClient.TextProtocol(msg)); tcpclient.Send(msgProtocol.ToBytes()); } } public static void OnConnected(object obj,TCPMessageArgs msg) { Console.WriteLine("Connected......."); tcpclient.Receive(); } public static void OnReceived(object obj, TCPMessageArgs msg) { //Console.WriteLine("Receivedd "+msg.state.Datagram); List<MsgProtocol> msgList = msg.state.MsgList; foreach(MsgProtocol msgProtocol in msgList) { if(msgProtocol.type==MsgType.TEXT) { Console.WriteLine(msgProtocol.textProtocol.content); } else { Console.WriteLine(msgProtocol.fileProtocol.filePath); } } } public static void OnDisConnected(object obj,TCPMessageArgs msg) { Console.WriteLine("Server Disconnected"); } } }
PS:未做客戶端短線重連