C#原生Socket服務器與客戶端的實現 C# Socket編程(1)基本的術語和概念 C# Socket編程(2)識別網絡主機 C# Socket編程(3)編碼和解碼 C# Socket編程(4)

  上個項目中用到了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 }
View Code

接下來實現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 }
View Code

接下來測試工具

  由於咱們在本地測試,因此使用迴環地址爲服務的監聽地址(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通信的學習,這幾篇博客對我幫助很大,書面感謝,我也在此記錄一下,說不定之後忘了,在回來看看

C# Socket編程(1)基本的術語和概念

C# Socket編程(2)識別網絡主機

C# Socket編程(3)編碼和解碼

C# Socket編程(4)初識Socket和數據流

C# Socket編程(5)使用TCP Socket

相關文章
相關標籤/搜索