Socket程式設計實現服務端和客戶端的互動
阿新 • • 發佈:2019-01-24
用Socket實現網路程式設計首先要建立一個Socket物件,Socket類位於System.Net.Socket名稱空間,需要先行匯入。建立Socket物件需要以下三個引數,這些引數都是列舉型別:
①AddressFamily成員指定Socket用來解析地址的定址方案,例如:InternetWork指示當Socket使用一個IP版本4地址連線;
②SocketType定義一個要開啟的Socket型別;
③Socket類使用ProtocolType列舉向Windows Sockets API通知所請求的協議。
如下程式碼為服務端建立Socket的程式碼:
- public partial class
- {
- Socket listenSocket = null; //伺服器端負責監聽的套接字
- Thread connThread = null; //負責監聽客戶端連線請求的執行緒
- public FrmService()
- {
- InitializeComponent();
- TextBox.CheckForIllegalCrossThreadCalls = false; //關閉對文字框的跨執行緒操作檢查
- }
- private void btnBeginListen_Click(object sender, EventArgs e)
- {
- //建立伺服器端負責監聽的套接字,引數(使用IP4定址協議,使用流式連線,使用TCP協議傳輸資料)
- listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- //用文字框中的IP地址建立一個IP地址物件IPAddress
- IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
- //建立一個包含IP地址和埠的網路節點物件
- IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
- //把網路節點物件繫結到負責監聽的Socket上
- listenSocket.Bind(endpoint);
- //設定監聽佇列的長度,也就是當前可以同時監聽的請求次數
- listenSocket.Listen(10);
- //建立負責監聽的執行緒物件
- connThread = new Thread(WatchConnection);
- //把該執行緒設定為後臺執行緒
- connThread.IsBackground = true;
- //開啟執行緒
- connThread.Start();
- txtShow.Text = "服務端啟動監聽成功!";
- }
- /// <summary>
- /// 監聽客戶端連線事件
- /// </summary>
- private void WatchConnection()
- {
- while (true) //使用一個死迴圈持續不斷的監聽新的客戶端的連線請求
- {
- //開始監聽客戶端的連線請求
- Socket connSocket = listenSocket.Accept();
- ShowMsg("客戶端連線成功!");
- }
- }
- private void ShowMsg(string message)
- {
- txtShow.Text = message + "\r\n";
- }
- }
服務端負責監聽的Socket在使用Accept()方法監聽到客戶端的連線請求之後,會馬上返回一個新的Socket來用於和這個客戶端進行通訊。而又由於客戶端的數量不確定,因此這裡需要一個集合來專門儲存Accept()方法返回的與客戶端進行通訊的Socket。這樣才能讓該服務端同時連線上多個客戶端。
經過改善後的程式碼如下:
- public partial class FrmService : Form //服務端窗體
- {
- Socket listenSocket = null; //伺服器端負責監聽的套接字
- Thread connThread = null; //負責監聽連線的執行緒
- Dictionary<string, Thread> recThreads = new Dictionary<string, Thread>(); //專門負責接收訊息的執行緒
- //Socket connSocket = null; //服務端負責與客戶端通訊的Socket
- //專門用於儲存//服務端負責與客戶端通訊的Socket的集合
- Dictionary<string, Socket> connSockets = new Dictionary<string, Socket>();
- IPEndPoint endpoint = null; //服務端的IP埠
- public FrmService()
- {
- InitializeComponent();
- TextBox.CheckForIllegalCrossThreadCalls = false; //關閉對文字框的跨執行緒操作檢查
- }
- private void btnBeginListen_Click(object sender, EventArgs e)
- {
- //建立伺服器端負責監聽的套接字,引數(使用IP4定址協議,使用流式連線,使用TCP協議傳輸資料)
- listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- //用文字框中的IP地址建立一個IP地址物件IPAddress
- IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
- //建立一個包含IP地址和埠的網路節點物件
- endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
- //把網路節點物件繫結到負責監聽的Socket上
- listenSocket.Bind(endpoint);
- //設定監聽佇列的長度,也就是當前可以同時監聽的請求次數
- listenSocket.Listen(10);
- //建立負責監聽的執行緒物件
- connThread = new Thread(WatchConnection);
- //把該執行緒設定為後臺執行緒
- connThread.IsBackground = true;
- //開啟執行緒
- connThread.Start();
- ShowMsg("服務端啟動監聽成功!");
- }
- /// <summary>
- /// 監聽客戶端連線事件
- /// </summary>
- private void WatchConnection()
- {
- while (true) //使用一個死迴圈持續不斷的監聽新的客戶端的連線請求
- {
- //開始監聽客戶端的連線請求
- Socket connSocket = listenSocket.Accept();
- //向ListBox中新增一個IP埠字串,作為訪問該客戶端的唯一標誌
- lbUniqueSign.Items.Add(connSocket.RemoteEndPoint.ToString());
- //將與客戶端通訊的Socket新增到集合中
- connSockets.Add(connSocket.RemoteEndPoint.ToString(), connSocket);
- Thread thread = new Thread(ReciveMessage);
- thread.IsBackground = true;
- //以IP埠字串為Key值,把接收訊息的執行緒新增到recThread集合中。
- recThreads.Add(connSocket.RemoteEndPoint.ToString(), thread);
- thread.Start(connSocket); //傳入引數,這個引數是當前負責與這個客戶端進行通訊的Socket
- ShowMsg("客戶端連線成功!" + connSocket.RemoteEndPoint.ToString());
- }
- }
- private void ShowMsg(string message)
- {
- txtShow.AppendText(message + "\r\n");
- }
- /// <summary>
- /// 傳送訊息按鈕
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnSendMsg_Click(object sender, EventArgs e)
- {
- //把傳送的訊息轉換為位元組陣列
- byte[] arrSendMsg = Encoding.UTF8.GetBytes(txtMessage.Text.Trim());
- //按照選定的IP埠,把訊息傳送到該客戶端上
- connSockets[lbUniqueSign.SelectedItem.ToString()].Send(arrSendMsg);
- //顯示訊息
- ShowMsg(endpoint.ToString() + "傳送訊息:");
- ShowMsg("\t"+txtMessage.Text.Trim());
- }
- /// <summary>
- /// 迴圈接收客戶端傳送過來的資料
- /// </summary>
- private void ReciveMessage(object socketParam) //接收訊息的方法需要一個object型別的引數
- {
- Socket socketClient = socketParam as Socket; //把object型別轉換為Socket型別
- while (true)
- {
- //宣告一個2M空間的位元組陣列
- byte[] arrRecMsg = new byte[1024 * 1024 * 2];
- //把接收到的位元組存入位元組陣列中,並獲取接收到的位元組數
- int length = socketClient.Receive(arrRecMsg);
- //按照接收到的實際位元組數獲取傳送過來的訊息
- ShowMsg(socketClient.RemoteEndPoint.ToString() + ":");
- ShowMsg("\t" + Encoding.UTF8.GetString(arrRecMsg, 0, length));
- }
- }
- }
以上程式碼在改善之後,又添加了一個傳送訊息方法。傳送訊息需要呼叫Socket的Send()方法,它需要一個byte型別的陣列引數。我們需要先把要傳送的訊息轉換為byte型別的陣列,使用Encoding.UTF8.GetBytes()方法,傳入訊息引數即可,然後把這個陣列傳入Send()方法中。上面程式碼還實現了接收客戶端資訊的功能,我們把接收訊息也放在一個單獨的後臺執行緒中。(具體原因在下面)
我們需要從客戶端去連線服務端,因此在客戶端,我們需要建立一個專門連線服務端的Socket,並傳入服務端的IP埠。程式碼如下:
- public partial class FrmClient : Form //客戶端窗體
- {
- private Thread recThread = null; //負責持續獲取服務端發來的訊息
- private Socket socketClient = null; //建立客戶端負責連線的Socket物件
- public FrmClient()
- {
- InitializeComponent();
- TextBox.CheckForIllegalCrossThreadCalls = false;
- }
- private void btnConnectService_Click(object sender, EventArgs e)
- {
- //建立一個IPAddress物件
- IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
- //建立一個包含IP地址和埠的網路節點IPEndPoint物件
- IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
- //建立Socket物件
- socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- //傳入網路節點,連線服務端
- socketClient.Connect(endpoint);
- //建立獲取訊息的後臺執行緒物件
- recThread = new Thread(ReciveMessage);
- recThread.IsBackground = true;
- //開啟執行緒
- recThread.Start();
- }
- /// <summary>
- /// 迴圈接收服務端傳送過來的資料
- /// </summary>
- private void ReciveMessage()
- {
- while (true)
- {
- //宣告一個2M空間的位元組陣列
- byte[] arrRecMsg = new byte[1024 * 1024 * 2];
- //把接收到的位元組存入位元組陣列中,並獲取接收到的位元組數
- int length = socketClient.Receive(arrRecMsg);
- //按照接收到的實際位元組數獲取傳送過來的訊息
- ShowMsg(socketClient.RemoteEndPoint.ToString() + ":");
- ShowMsg("\t" + Encoding.UTF8.GetString(arrRecMsg, 0, length));
- }
- }
- private void ShowMsg(string message)
- {
- txtShow.AppendText(message + "\r\n");
- }
- /// <summary>
- /// 傳送訊息按鈕
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnSendMsg_Click(object sender, EventArgs e)
- {
- byte[] arrSendMsg = Encoding.UTF8.GetBytes(txtMessage.Text.Trim());
- socketClient.Send(arrSendMsg);
- ShowMsg("客戶端" + "傳送訊息:");
- ShowMsg("\t"+txtMessage.Text.Trim());
- }
- }
同時啟動服務端和客戶端兩個程式(先啟動其中一個專案,然後在解決方案管理器的另外一個專案名上單擊右鍵,在彈出的快捷選單中選擇“除錯”裡面的“啟動新例項”項並單擊),執行結果如下圖:
博文地址:http://blog.csdn.net/u011416044/article/details/12004819