上個項目中用到了Socket通信,項目中直接藉助SuperSocket實現,可是我以爲這畢竟是一個我沒接觸過的東西,因此也順便學習了一下原生socket的使用,作了一個socket服務器與客戶端的開發.本人菜鳥一枚,只作了一個簡單的實現,但願有看到我博客的大佬不吝指點,抱拳!html
socket通信的相關知識的話,在博客園中的大佬們總結的貼子已經很是多,也很詳細,忘記了就在去看。編程
這裏總結一下原生的Socket和SuperSocket的使用(官方定義:SuperSocket 是一個輕量級, 跨平臺並且可擴展的 .Net/Mono Socket 服務器程序框架。你無須瞭解如何使用 Socket, 如何維護 Socket 鏈接和 Socket 如何工做,可是你卻可使用 SuperSocket 很容易的開發出一款 Socket 服務器端軟件,例如遊戲服務器,GPS 服務器, 工業控制服務和數據採集服務器等等。)c#
下邊是一個Socket測試工具 十分好用!服務器
連接:https://pan.baidu.com/s/1ykEofUIZKE2yhe3mMyRbJw
提取碼:m2nk網絡
先從服務器端提及。服務器端先初始化Socket,而後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端鏈接。在這時若是有個客戶端初始化一個Socket,而後鏈接服務器(connect),若是鏈接成功,這時客戶端與服務器端的鏈接就創建了。客戶端發送數據請求,服務器端接收請求並處理請求,而後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉鏈接,一次交互結束。框架
原生Socket實現SocketServer:socket
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using System.Net.Sockets; 11 using System.Net; 12 using System.Threading; 13 14 namespace DotnetSockets 15 { 16 public partial class DotnetSocketServer : Form 17 { 18 public DotnetSocketServer() 19 { 20 InitializeComponent(); 21 } 22 23 //存儲已鏈接的客戶端的泛型集合 24 private static Dictionary<string, Socket> socketList = new Dictionary<string, Socket>(); 25 26 /// <summary> 27 /// 接收鏈接 28 /// </summary> 29 /// <param name="obj"></param> 30 public void StartServer(object obj) 31 { 32 string str; 33 while (true) 34 { 35 //等待接收客戶端鏈接 Accept方法返回一個用於和該客戶端通訊的Socket 36 Socket recviceSocket = ((Socket)obj).Accept(); 37 //獲取客戶端ip和端口號 38 str = recviceSocket.RemoteEndPoint.ToString(); 39 socketList.Add(str, recviceSocket); 40 //控件調用invoke方法 解決"從不是建立控件的線程訪問它"的異常 41 cmb_socketlist.Invoke(new Action(() => { cmb_socketlist.Items.Add(str); })); 42 richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(str + "已鏈接" + "\r\n"); })); 43 44 //Accept()執行事後 當前線程會阻塞 只有在有客戶端鏈接時纔會繼續執行 45 //建立新線程,監控接收新客戶端的請求數據 46 Thread thread = new Thread(startRecive); 47 thread.IsBackground = true; 48 thread.Start(recviceSocket); 49 } 50 } 51 52 /// <summary> 53 /// 接收消息 54 /// </summary> 55 /// <param name="obj">客戶端socket</param> 56 public void startRecive(object obj) 57 { 58 string str; 59 string ip; 60 while (true) 61 { 62 63 byte[] buffer = new byte[2048]; 64 int count; 65 try 66 { 67 //Receive(Byte[]) 從綁定的 Socket 套接字接收數據,將數據存入接收緩衝區。 68 //該方法執行事後同Accept()方法同樣 當前線程會阻塞 等到客戶端下一次發來數據時繼續執行 69 count = ((Socket)obj).Receive(buffer); 70 ip = ((Socket)obj).RemoteEndPoint.ToString(); 71 if (count == 0) 72 { 73 cmb_socketlist.Invoke(new Action(() => { cmb_socketlist.Items.Remove(ip); })); 74 richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(ip + "已斷開鏈接" + "\r\n"); })); 75 break; 76 } 77 else 78 { 79 str = Encoding.Default.GetString(buffer, 0, count); 80 richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("收到"+ip+"數據 " + str + "\r\n"); })); 81 82 } 83 } 84 catch (Exception) 85 { 86 87 88 } 89 } 90 } 91 92 /// <summary> 93 /// 開啓服務器監聽 94 /// </summary> 95 /// <param name="sender"></param> 96 /// <param name="e"></param> 97 private void btn_StartListen_Click(object sender, EventArgs e) 98 { 99 //實例化一個Socket對象,肯定網絡類型、Socket類型、協議類型 100 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 101 102 IPEndPoint IEP = new IPEndPoint(IPAddress.Parse(txt_ip.Text), int.Parse(txt_port.Text)); 103 //綁定ip和端口 104 socket.Bind(IEP); 105 //開啓監聽 106 socket.Listen(10); 107 108 richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("開始監聽" + "\r\n"); })); 109 110 Thread thread = new Thread(new ParameterizedThreadStart(StartServer)); 111 thread.IsBackground = true; 112 thread.Start(socket); 113 114 115 #region 該部分實現只適用一個服務器只對應一個客戶端 116 117 //Task.Run(() => { 118 119 // string str; 120 121 // while (true) 122 // { 123 // //等待接收客戶端鏈接 Accept返回一個用於和該客戶端通訊的Socket 124 // Socket recviceSocket = socket.Accept(); 125 126 // //Accept()執行事後 當前線程會暫時掛起 只有在有客戶端鏈接時纔會繼續執行 127 // richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(recviceSocket.RemoteEndPoint.ToString() + "已鏈接" + "\r\n"); })); 128 129 // //開啓接收數據的任務 130 // Task.Run(() => { 131 // while (true) 132 // { 133 // byte[] buffer = new byte[2048]; 134 // int count; 135 // //Receive(Byte[]) 從綁定的 Socket 套接字接收數據,將數據存入接收緩衝區。 136 // //該方法執行事後同上 當前線程會暫時掛起 等到客戶端下一次發來數據時繼續執行 137 // count = recviceSocket.Receive(buffer); 138 // if (count == 0) 139 // { 140 // richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(recviceSocket.RemoteEndPoint.ToString() + "已斷開鏈接" + "\r\n"); })); 141 142 // break; 143 // } 144 // else 145 // { 146 // str = Encoding.Default.GetString(buffer, 0, count); 147 // richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("收到"+recviceSocket.RemoteEndPoint.ToString()+"數據:" + str + "\r\n"); })); 148 149 // } 150 // } 151 152 153 // }); 154 155 156 // } 157 //}); 158 #endregion 159 } 160 161 /// <summary> 162 /// 向對應客戶端發送數據 163 /// </summary> 164 /// <param name="sender"></param> 165 /// <param name="e"></param> 166 private void btn_send_Click(object sender, EventArgs e) 167 { 168 string str = txt_send.Text; 169 byte[] bytes = new byte[2048]; 170 bytes = Encoding.Default.GetBytes(str); 171 //獲取combobox的值 從泛型集合中獲取對應的客戶端socket 而後發送數據 172 if (cmb_socketlist.Items.Count != 0) 173 { 174 if (cmb_socketlist.SelectedItem == null) 175 { 176 MessageBox.Show("請選擇一個客戶端發送數據!"); 177 return; 178 } 179 else 180 { 181 socketList[cmb_socketlist.SelectedItem.ToString()].Send(bytes); 182 } 183 } 184 else 185 { 186 richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("當前無鏈接的客戶端" + "\r\n"); })); 187 } 188 txt_send.Clear(); 189 } 190 191 private void DotnetSocketServer_FormClosed(object sender, FormClosedEventArgs e) 192 { 193 System.Environment.Exit(0); 194 } 195 } 196 }
接下來實現SocketClientide
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using System.Net; 11 using System.Net.Sockets; 12 using System.Threading; 13 14 namespace DotnetSocketClient 15 { 16 public partial class DotnetSocketClient : Form 17 { 18 public DotnetSocketClient() 19 { 20 InitializeComponent(); 21 } 22 byte[] buffer = new byte[2048]; 23 Socket socket; 24 Thread thread; 25 26 /// <summary> 27 /// 鏈接服務器 28 /// </summary> 29 /// <param name="sender"></param> 30 /// <param name="e"></param> 31 private void btn_start_Click(object sender, EventArgs e) 32 { 33 try 34 { 35 //實例化socket 36 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 37 //鏈接服務器 38 socket.Connect(new IPEndPoint(IPAddress.Parse(txt_ip.Text), int.Parse(txt_port.Text))); 39 40 thread = new Thread(StartReceive); 41 thread.IsBackground = true; 42 thread.Start(socket); 43 } 44 catch (Exception ex) 45 { 46 SetMessage("服務器異常:" + ex.Message); 47 } 48 49 } 50 /// <summary> 51 /// 開啓接收 52 /// </summary> 53 /// <param name="obj"></param> 54 private void StartReceive(object obj) 55 { 56 string str; 57 while (true) 58 { 59 Socket receiveSocket = obj as Socket; 60 try 61 { 62 int result = receiveSocket.Receive(buffer); 63 if (result == 0) 64 { 65 break; 66 } 67 else 68 { 69 str = Encoding.Default.GetString(buffer); 70 SetMessage("接收到服務器數據: " + str); 71 } 72 73 } 74 catch (Exception ex) 75 { 76 SetMessage("服務器異常:" + ex.Message); 77 78 } 79 } 80 81 } 82 /// <summary> 83 /// 關閉鏈接 84 /// </summary> 85 /// <param name="sender"></param> 86 /// <param name="e"></param> 87 private void btn_close_Click(object sender, EventArgs e) 88 { 89 try 90 { 91 socket.Shutdown(SocketShutdown.Both); 92 socket.Close(); 93 thread.Abort(); 94 SetMessage("關閉與遠程服務器的鏈接!"); 95 } 96 catch (Exception ex) 97 { 98 SetMessage("異常" + ex.Message); 99 } 100 } 101 102 private void button1_Click(object sender, EventArgs e) 103 { 104 socket.Send(Encoding.Default.GetBytes(txt_send.Text)); 105 txt_send.Clear(); 106 } 107 /// <summary> 108 /// 添加信息 109 /// </summary> 110 /// <param name="msg"></param> 111 private void SetMessage(string msg) 112 { 113 richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(msg+"\r\n"); })); 114 } 115 } 116 }
接下來測試工具
由於咱們在本地測試,因此使用迴環地址爲服務的監聽地址(127.0.0.1),端口號範圍(0~65535)但不能和正在使用的端口號衝突,因此儘可能設置大一些的值,本次測試使用端口號 3333,post
這裏總結一個查看正在使用的端口號的方法:
win+R打開命令提示符,而後輸入 netstat -a -n 回車,會列出當前正在使用的協議,內部地址,外部地址和狀態
服務器端 輸入ip ,輸入端口,開啓監聽 ,客戶端輸入服務器ip和端口, 點擊開始鏈接
客戶端
能夠看到,客戶端點擊鏈接之後服務器已收到客戶端的鏈接,並做出提示,而且未來訪客戶端的ip和端口號記錄
接下來測試 互相發送數據
服務器給客戶端發送數據:
從客戶端列選擇對應客戶端ip,而後從下邊textbox 輸入要發送的數據,點擊發送
服務器:
客戶端:
客戶端給服務器發送數據:
從客戶端的textbox中輸入要發送的數據,點擊發送
服務器:
客戶端
通常狀況下,都不會是服務器與客戶端一對一的數據交互,接下來 咱們藉助上邊推薦的工具,測試一下多個客戶端訪問服務器
首先在SocketTool上建立多個客戶端,咱們能夠清楚的看到雖然建立多個客戶端,可是端口號都不同.
而後將這三個客戶端所有鏈接服務器
接下來測試數據交互
三個客戶端分別給服務器端發送111,222,333
服務器
服務器選擇端口號爲7156的客戶端發送數據aaaa
服務器
客戶端
通過上面測試,簡直完美(斜眼笑),果真,每作完一個東西所產生的成就感仍是使人心情舒暢啊
原生socket開發實現暫時結束,若是有遺忘的,後續更新補充,
感謝各位客官閱讀,拜謝(抱拳~)
接下來還要再寫一篇 使用Socket服務器程序框架實現SuperSocket實現服務器
socket通信的學習,這幾篇博客對我幫助很大,書面感謝,我也在此記錄一下,說不定之後忘了,在回來看看