參考http://bbs.cskin.net/thread-326-1-1.html的大神的代碼
socket封裝
/// <summary> /// 自定義Socket對象 /// </summary> public class Sockets { /// <summary> /// 空構造 /// </summary> public Sockets() { } /// <summary> /// 創建Sockets對象 /// </summary> /// <param name="ip">Ip地址</param> /// <param name="client">TcpClient</param> /// <param name="ns">承載客戶端Socket的網絡流</param> public Sockets(IPEndPoint ip, TcpClient client, NetworkStream ns) { Ip = ip; Client = client; nStream = ns; } /// <summary> /// 創建Sockets對象 /// </summary> /// <param name="name">用戶名</param> /// <param name="pass">密碼</param> /// <param name="ip">Ip地址</param> /// <param name="client">TcpClient</param> /// <param name="ns">承載客戶端Socket的網絡流</param> public Sockets(string name, string pass, IPEndPoint ip, TcpClient client, NetworkStream ns) { UserName = name; Password = pass; Ip = ip; Client = client; nStream = ns; } /// <summary> /// 接收緩沖區 /// </summary> public byte[] RecBuffer = new byte[8 * 1024]; /// <summary> /// 發送緩沖區 /// </summary> public byte[] SendBuffer = new byte[8 * 1024]; /// <summary> /// 異步接收后包的大小 /// </summary> public int Offset { get; set; } /// <summary> /// 用戶名 /// </summary> public string UserName { get; set; } /// <summary> /// 密碼 /// </summary> public string Password { get; set; } /// <summary> /// 當前IP地址,端口號 /// </summary> public IPEndPoint Ip { get; set; } /// <summary> /// 客戶端主通信程序 /// </summary> public TcpClient Client { get; set; } /// <summary> /// 承載客戶端Socket的網絡流 /// </summary> public NetworkStream nStream { get; set; } /// <summary> /// 發生異常時不為null. /// </summary> public Exception ex { get; set; } /// <summary> /// 新客戶端標識.如果推送器發現此標識為true,那么認為是客戶端上線 /// 僅服務端有效 /// </summary> public bool NewClientFlag { get; set; } /// <summary> /// 客戶端退出標識.如果服務端發現此標識為true,那么認為客戶端下線 /// 客戶端接收此標識時,認為客戶端異常. /// </summary> public bool ClientDispose { get; set; } } /// <summary> /// Socket基類(抽象類) /// 抽象3個方法,初始化Socket(含一個構造),停止,啟動方法. /// 此抽象類為TcpServer與TcpClient的基類,前者實現后者抽象方法. /// 作用: 純屬閑的蛋疼,不寫個OO的我感覺不會寫代碼了...What The Fuck... /// </summary> public abstract class SocketObject { public abstract void InitSocket(IPAddress ipaddress, int port); public abstract void InitSocket(string ipaddress, int port); public abstract void Start(); public abstract void Stop(); }
委托傳消息
public delegate void PushSockets(Sockets sockets);
服務端
/// <summary> /// Tcp同步服務端,SocketObject繼承抽象類 /// 服務端采用TcpListener封裝. /// 使用Semaphore 來控制並發,每次處理5個.最大處理5000 /// </summary> public class TcpServer : SocketObject { public PushSockets pushSockets; bool IsStop = false; object obj = new object(); /// <summary> /// 信號量 /// </summary> private Semaphore semap = new Semaphore(5, 5000); /// <summary> /// 客戶端隊列集合 /// </summary> public List<Sockets> ClientList = new List<Sockets>(); /// <summary> /// 服務端 /// </summary> private TcpListener listener; /// <summary> /// 當前IP地址 /// </summary> private IPAddress Ipaddress; /// <summary> /// 歡迎消息 /// </summary> public string boundary = ""; /// <summary> /// 當前監聽端口 /// </summary> private int Port; /// <summary> /// 當前IP,端口對象 /// </summary> private IPEndPoint ip; /// <summary> /// 初始化服務端對象 /// </summary> /// <param name="ipaddress">IP地址</param> /// <param name="port">監聽端口</param> public override void InitSocket(IPAddress ipaddress, int port) { Ipaddress = ipaddress; Port = port; listener = new TcpListener(Ipaddress, Port); } /// <summary> /// 初始化服務端對象 /// </summary> /// <param name="ipaddress">IP地址</param> /// <param name="port">監聽端口</param> public override void InitSocket(string ipaddress, int port) { Ipaddress = IPAddress.Parse(ipaddress); Port = port; ip = new IPEndPoint(Ipaddress, Port); listener = new TcpListener(Ipaddress, Port); } /// <summary> /// 啟動監聽,並處理連接 /// </summary> public override void Start() { try { listener.Start(); Thread AccTh = new Thread(new ThreadStart(delegate { while (true) { if (IsStop != false) { break; } GetAcceptTcpClient(); Thread.Sleep(1); } })); AccTh.Start(); } catch (SocketException skex) { Sockets sks = new Sockets(); sks.ex = skex; if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } } /// <summary> /// 停止 /// </summary> public override void Stop() { if (listener != null) { listener.Stop(); listener = null; IsStop = true; pushSockets = null; } } /// <summary> /// 等待處理新的連接 /// </summary> private void GetAcceptTcpClient() { try { if (listener.Pending()) { semap.WaitOne(); TcpClient tclient = listener.AcceptTcpClient(); //維護客戶端隊列 Socket socket = tclient.Client; NetworkStream stream = new NetworkStream(socket, true); //承載這個Socket Sockets sks = new Sockets(tclient.Client.RemoteEndPoint as IPEndPoint, tclient, stream); sks.NewClientFlag = true; //推送新客戶端 if (pushSockets != null) pushSockets.Invoke(sks); //客戶端異步接收 sks.nStream.BeginRead(sks.RecBuffer, 0, sks.RecBuffer.Length, new AsyncCallback(EndReader), sks); //加入客戶端集合. AddClientList(sks); //主動向客戶端發送一條連接成功信息 //if (stream.CanWrite) //{ // if (!string.IsNullOrEmpty(boundary)) // { // byte[] buffer = Encoding.UTF8.GetBytes(boundary); // stream.Write(buffer, 0, buffer.Length); // } //} semap.Release(); } } catch { return; } } /// <summary> /// 異步接收發送的信息. /// </summary> /// <param name="ir"></param> private void EndReader(IAsyncResult ir) { Sockets sks = ir.AsyncState as Sockets; if (sks != null && listener != null) { try { if (sks.NewClientFlag || sks.Offset != 0) { sks.NewClientFlag = false; sks.Offset = sks.nStream.EndRead(ir); if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI sks.nStream.BeginRead(sks.RecBuffer, 0, sks.RecBuffer.Length, new AsyncCallback(EndReader), sks); } } catch (Exception skex) { lock (obj) { //移除異常類 ClientList.Remove(sks); Sockets sk = sks; sk.ClientDispose = true;//客戶端退出 sk.ex = skex; if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } } } } /// <summary> /// 加入隊列. /// </summary> /// <param name="sk"></param> private void AddClientList(Sockets sk) { //雖然有信號量,還是用lock增加系數 lock (obj) { Sockets sockets = ClientList.Find(o => { return o.Ip == sk.Ip; }); //如果不存在則添加,否則更新 if (sockets == null) { ClientList.Add(sk); } else { ClientList.Remove(sockets); ClientList.Add(sk); } } } /// <summary> /// 向所有在線的客戶端發送信息. /// </summary> /// <param name="SendData">發送的文本</param> public void SendToAll(string SendData) { try { Parallel.ForEach(ClientList, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, item => { if (item != null) SendToClient(item.Ip, SendData); }); } catch (Exception ex) { //Console.Write(ex.Message); } } /// <summary> /// 向某一位客戶端發送信息 /// </summary> /// <param name="ip">客戶端IP+端口地址</param> /// <param name="SendData">發送的數據包</param> public void SendToClient(IPEndPoint ip, string SendData) { try { Sockets sks = ClientList.Find(o => { return o.Ip == ip; }); if (sks == null || !sks.Client.Connected || sks.ClientDispose) { //沒有連接時,標識退出 Sockets ks = new Sockets(); sks.ClientDispose = true;//標識客戶端下線 sks.ex = new Exception("客戶端無連接"); if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI ClientList.Remove(sks); } if (sks.Client.Connected) { //獲取當前流進行寫入. NetworkStream nStream = sks.nStream; if (nStream.CanWrite) { byte[] buffer = Encoding.UTF8.GetBytes(SendData); nStream.Write(buffer, 0, buffer.Length); } else { //避免流被關閉,重新從對象中獲取流 nStream = sks.Client.GetStream(); if (nStream.CanWrite) { byte[] buffer = Encoding.UTF8.GetBytes(SendData); nStream.Write(buffer, 0, buffer.Length); } else { //如果還是無法寫入,那么認為客戶端中斷連接. ClientList.Remove(sks); Sockets ks = new Sockets(); sks.ClientDispose = true;//如果出現異常,標識客戶端下線 sks.ex = new Exception("客戶端無連接"); if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } } } } catch (Exception skex) { Sockets sks = new Sockets(); sks.ClientDispose = true;//如果出現異常,標識客戶端退出 sks.ex = skex; if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } } }
客戶端
public class TcpClients : SocketObject { public PushSockets pushSockets; bool IsClose = false; /// <summary> /// 當前管理對象 /// </summary> Sockets sk; /// <summary> /// 客戶端 /// </summary> TcpClient client; /// <summary> /// 當前連接服務端地址 /// </summary> IPAddress Ipaddress; /// <summary> /// 當前連接服務端端口號 /// </summary> int Port; /// <summary> /// 服務端IP+端口 /// </summary> IPEndPoint ip; /// <summary> /// 發送與接收使用的流 /// </summary> NetworkStream nStream; /// <summary> /// 初始化Socket /// </summary> /// <param name="ipaddress"></param> /// <param name="port"></param> public override void InitSocket(IPAddress ipaddress, int port) { Ipaddress = ipaddress; Port = port; ip = new IPEndPoint(Ipaddress, Port); client = new TcpClient(); } /// <summary> /// 初始化Socket /// </summary> /// <param name="ipaddress">ipd地址</param> /// <param name="port">端口</param> public override void InitSocket(string ipaddress, int port) { Ipaddress = IPAddress.Parse(ipaddress); Port = port; ip = new IPEndPoint(Ipaddress, Port); client = new TcpClient(); } /// <summary> /// 重寫Start方法,其實就是連接服務端 /// </summary> public override void Start() { Connect(); } /// <summary> /// 連接 /// </summary> private void Connect() { client.Connect(ip); nStream = new NetworkStream(client.Client, true); sk = new Sockets(ip, client, nStream); sk.nStream.BeginRead(sk.RecBuffer, 0, sk.RecBuffer.Length, new AsyncCallback(EndReader), sk); } /// <summary> /// 讀取 /// </summary> private void EndReader(IAsyncResult ir) { Sockets s = ir.AsyncState as Sockets; try { if (s != null) { if (IsClose && client == null) { sk.nStream.Close(); sk.nStream.Dispose(); return; } s.Offset = s.nStream.EndRead(ir); if (pushSockets != null) pushSockets.Invoke(s);//推送至UI sk.nStream.BeginRead(sk.RecBuffer, 0, sk.RecBuffer.Length, new AsyncCallback(EndReader), sk); } } catch (Exception skex) { Sockets sks = s; sks.ex = skex; sks.ClientDispose = true; if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } } /// <summary> /// 停止 /// </summary> public override void Stop() { Sockets sks = new Sockets(); try { if (client != null) { client.Client.Shutdown(SocketShutdown.Both); Thread.Sleep(10); client.Close(); IsClose = true; client = null; } else { sks.ex = new Exception("客戶端沒有初始化.!"); } if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } catch (Exception ex) { sks.ex = ex; } } /// <summary> /// 發送消息 /// </summary> public void SendData(string SendData) { try { if (client == null || !client.Connected) { Sockets sks = new Sockets(); sks.ex = new Exception("客戶端無連接.."); sks.ClientDispose = true; if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } if (client.Connected) //如果連接則發送 { if (nStream == null) { nStream = client.GetStream(); } byte[] buffer = Encoding.UTF8.GetBytes(SendData); nStream.Write(buffer, 0, buffer.Length); } } catch (Exception skex) { Sockets sks = new Sockets(); sks.ex = skex; sks.ClientDispose = true; if (pushSockets != null) pushSockets.Invoke(sks);//推送至UI } } }
加密消息的類
/// <summary> /// 數據DES加密 /// </summary> public class Encrypt { private byte[] iba_mIV = new byte[8]; //向量 private byte[] iba_mKey = new byte[8]; //密鑰 private DESCryptoServiceProvider io_DES = new DESCryptoServiceProvider(); public Encrypt() { this.iba_mKey[0] = 0x95; this.iba_mKey[1] = 0xc4; this.iba_mKey[2] = 0xf6; this.iba_mKey[3] = 0x49; this.iba_mKey[4] = 0xac; this.iba_mKey[5] = 0x61; this.iba_mKey[6] = 0xa3; this.iba_mKey[7] = 0xe2; this.iba_mIV[0] = 0xf9; this.iba_mIV[1] = 0x6a; this.iba_mIV[2] = 0x65; this.iba_mIV[3] = 0xb8; this.iba_mIV[4] = 0x4a; this.iba_mIV[5] = 0x23; this.iba_mIV[6] = 0xfe; this.iba_mIV[7] = 0xc6; this.io_DES.Key = this.iba_mKey; this.io_DES.IV = this.iba_mIV; } /// <summary> /// 初始化加密向量與密鑰 長度為8 /// </summary> /// <param name="iba_mIV">向量</param> /// <param name="iba_mKey">密鑰</param> public Encrypt(byte[] iba_mIV, byte[] iba_mKey) { this.io_DES.IV = iba_mIV; this.io_DES.Key = iba_mKey; } /// <summary> /// 解密 /// </summary> /// <param name="as_Data"></param> /// <returns></returns> public string doDecrypt(string as_Data) { ICryptoTransform lo_ICT = this.io_DES.CreateDecryptor(this.io_DES.Key, this.io_DES.IV); try { byte[] lba_bufIn = this.FromHexString(as_Data);//Encoding.UTF8.GetString(Convert.FromBase64String( byte[] lba_bufOut = lo_ICT.TransformFinalBlock(lba_bufIn, 0, lba_bufIn.Length); return Encoding.UTF8.GetString(lba_bufOut); } catch { return as_Data; } } /// <summary> /// 加密 /// </summary> /// <param name="as_Data"></param> /// <returns></returns> public string doEncrypt(string as_Data) { ICryptoTransform lo_ICT = this.io_DES.CreateEncryptor(this.io_DES.Key, this.io_DES.IV); try { byte[] lba_bufIn = Encoding.UTF8.GetBytes(as_Data); byte[] lba_bufOut = lo_ICT.TransformFinalBlock(lba_bufIn, 0, lba_bufIn.Length); return GetHexString(lba_bufOut);//Convert.ToBase64String(Encoding.UTF8.GetBytes(); } catch { return ""; } } /// <summary> /// 轉換2進制 /// </summary> /// <param name="as_value"></param> /// <returns></returns> private byte[] FromHexString(string as_value) { byte[] lba_buf = new byte[Convert.ToInt32((int)(as_value.Length / 2))]; for (int li_i = 0; li_i < lba_buf.Length; li_i++) { lba_buf[li_i] = Convert.ToByte(as_value.Substring(li_i * 2, 2), 0x10); } return lba_buf; } /// <summary> /// 字節轉字符串 /// </summary> /// <param name="aba_buf"></param> /// <returns></returns> private string GetHexString(byte[] aba_buf) { StringBuilder lsb_value = new StringBuilder(); foreach (byte lb_byte in aba_buf) { lsb_value.Append(Convert.ToString(lb_byte, 0x10).PadLeft(2, '0')); } return lsb_value.ToString(); } }
cs端控件刷新
public class ControlInvoker { public static void Invoke(Control ctl, ThreadStart thread) { try { if (!ctl.IsHandleCreated) return; if (ctl.IsDisposed) return; if (ctl.InvokeRequired) { ctl.Invoke(thread); } else { thread(); } } catch (Exception ex) { Console.Write(ex); } } }
服務端構建
private TcpServer _server; private void frmSocketServer_Load(object sender, EventArgs e) { _server = new TcpServer(); _server.pushSockets = Rec; } /// <summary> /// 委托刷新客戶數據 /// </summary> private void Rec(Sockets sks) { ControlInvoker.Invoke(this, new ThreadStart(delegate { //這個是存放消息的,不用管 if (txtClientState.Lines.Count() > 10) { string path = HandleTxt.CreateFile(AppDomain.CurrentDomain.BaseDirectory + @"log\", DateTime.Now);//創造路徑 HandleTxt.WriteTxt(path + @"\" + DateTime.Now.ToString("HHmmss") + ".txt", txtClientState.Lines.ToList()); txtClientState.Text = ""; } if (sks.ex != null) { //在此處理異常信息 if (sks.ClientDispose) { //客戶端非主動斷開連接下線. 非正常下線 if (sks.Ip != null) { RemoveClient(sks, string.Format("客戶端:{0}下線.{1}!\r\n", sks.Ip, sks.ex.Message)); } } } else { if (sks.NewClientFlag) { AddClient(sks, string.Format("新客戶端:{0}連接成功.!\r\n", sks.Ip)); } else if (sks.Offset == 0) { //正常是不會發送0包的,只有客戶端主動斷開連接時發送空包. //客戶端下線. RemoveClient(sks, string.Format("客戶端:{0}下線.!\r\n", sks.Ip)); } else { //這里是界面提示消息 //sks } } })); } /// <summary> /// 接收客戶端消息 /// </summary> private void ClientInfo(Sockets sks) { byte[] buffer = new byte[sks.Offset]; Array.Copy(sks.RecBuffer, buffer, sks.Offset); string str = Encoding.UTF8.GetString(buffer); } /// <summary> /// 啟動服務 /// </summary> private void StartServer() { try { _server.InitSocket(IPAddress.Any, 9527); _server.Start(); // ClientState("服務端啟動成功.!\r\n"); // SetEnabled(false); } catch (Exception ex) { //ClientState(string.Format("啟動失敗!原因:{0}", ex.Message)); // SetEnabled(true); } } /// <summary> /// 發送消息給客戶端 /// </summary> private void SendToClient(IPEndPoint ip, string SendData) { Task.Factory.StartNew(new Action(() => { Thread.Sleep(1000); _server.SendToClient(ip, SendData); })); } /// <summary> /// 推送消息 /// </summary> private void SendMess() { Task.Factory.StartNew(new Action(() => { _server.SendToAll("123"); })); }
客戶端接收及發送
private TcpClients _client; private void frmPlaySingleInfo_Load(object sender, EventArgs e) { Control.CheckForIllegalCrossThreadCalls = false; _client = new TcpClients(); _client.pushSockets = ReceiveMess;//注冊推送器 } /// <summary> /// 處理推送過來的消息 /// </summary> /// <param name="rec"></param> private void ReceiveMess(Sockets sks) { ControlInvoker.Invoke(this, new ThreadStart(delegate { if (sks.ex != null) { if (sks.ClientDispose == true) { //由於未知原因引發異常.導致客戶端下線. 比如網絡故障.或服務器斷開連接. //SetClientState(string.Format("客戶端下線.!異常消息:{0}\r\n", sks.ex)); } else { //SetClientState(string.Format("異常消息:{0}\r\n", sks.ex)); } //timerConnect.Enabled = true; } else if (sks.Offset == 0) { //客戶端主動下線 // SetClientState("客戶端下線.!"); } else { byte[] buffer = new byte[sks.Offset]; Array.Copy(sks.RecBuffer, buffer, sks.Offset); string str = Encoding.UTF8.GetString(buffer); if (sks.Client.Client.Available > 0)//判斷消息是否發送完成,socket的數據大小限制,分多個包發送 { Console.Write(str); _mess += str; } else { _mess += str; //接收完成消息后處理 。。。 _mess = ""; } } })); } /// <summary> /// 解析域名 /// </summary> private string DomainName(string strDomain) { IPHostEntry host = Dns.GetHostByName(strDomain); IPAddress ip = host.AddressList[0]; return ip.ToString(); } /// <summary> /// 設置端口等數據 /// 是否重連 /// </summary> private void SetData(PlaySingleXml model, bool isConn) { _ip = ExistIp(model.ip); _port = model.port; timer.Interval = model.minutes * 60 * 1000; if (isConn) ConnectSocket(); } /// <summary> /// 驗證ip地址是否是連接 /// </summary> private string ExistIp(string ip) { Match m = Regex.Match(ip, @"((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))"); // 匹配正則表達式 if (!m.Success) { return DomainName(ip); } else { return ip; } } /// <summary> /// 連接Socket /// </summary> private void ConnectSocket() { try { _client.InitSocket(_ip, _port); _client.Start(); //SetClientState("連接成功.!"); //timerConnect.Enabled = false; } catch (Exception ex) { //SetClientState(ex.Message); //timerConnect.Enabled = true; } }
這個socket通訊基本完成
最后再附上另一個大神的東西http://bbs.cskin.net/thread-874-1-1.html