TCP/IP協議學習(五) 基於C# Socket的C/S模型


  TCP/IP協議作為現代網絡通訊的基石,內容包羅萬象,直接去理解理論是比較困難的;然而通過實踐先理解網絡通訊的理解,在反過來理解學習TCP/IP協議棧就相對簡單很多。C#通過提供的Socket API實現了對TCP/IP協議棧的封裝,讓實現C/S模型變得更加簡單,對於入門TCP/IP協議學習十分有幫助。

   Socket通訊實現參考標准的流程如圖所示,

·

  服務器工作:

  1.創建Socket套接字,綁定指定host,並監聽。

    2.線程堵塞等待用戶端請求,當客戶端請求到達時建立連接。

  3.連接建立完成后,讀取請求並處理,然后將處理結果返回給客戶端。

  4. 服務器等待客戶端關閉連接,連接關閉后,一次Socket通訊結束。

    客戶端工作:

   1.創建Socket套接字, 建立連接,發送連接請求數據

    2. 請求成功后,等待服務器返回響應

    3.服務器回應成功后,處理數據,直到主動關閉連接。

  了解了上述Socket通訊的主要過程,在結合C#網絡編程的特性,就可以實現如下C/S服務器。客戶端的實現比較簡單,主要分四個步驟:

  1.建立tcp連接(等同於socket 和 connect),處理連接失敗狀態

    2.對於建立成功的連接,發送數據,並等待服務器返回

    3.接收數據,輸出到編輯框

    4.關閉連接

private void button1_Click(object sender, EventArgs e) { try { string s_ipaddress0 = textBox1.Text; string s_ipaddress1 = textBox2.Text; string s_ipaddress2 = textBox3.Text; string s_ipaddress3 = textBox4.Text; string s_port = textBox5.Text; //限定port的合法性
        bool isPort; int port; isPort = Int32.TryParse(s_port, out port); if (!(isPort && port >= 0 && port <= 65536)) { OutToClient("Port is Invalid!"); return; } //限定ip地址的合法性
        string host = s_ipaddress0 + "." + s_ipaddress1 + "." + s_ipaddress2 + "." + s_ipaddress3; if (!IsIpAdress(host)) { OutToClient("IpAdress is invaild!"); return; } IPAddress ip = IPAddress.Parse(host); IPEndPoint ipe = new IPEndPoint(ip, port); //Ip端口綁定 如192.168.1.48:80 //發送框不能為空, 不然會導致GetBytes出錯
        if (textBox6.Text == "") { OutToClient("Send Text is empty!"); return; } OutToClient("Conneting..."); int timeout = 1000; //tcpClient連接到指定端口,並處理超時
        TcpClient NetworkClient = TimeOutSocket.Connect(ipe, timeout); NetworkStream ntwStream = NetworkClient.GetStream(); if (!ntwStream.CanWrite) { OutToClient("Data can not be write!"); return; } //向綁定地址端口發送數據
         OutToClient("Client send:" + textBox6.Text); string sendStr = textBox6.Text; byte[] by_send = Encoding.ASCII.GetBytes(sendStr); ntwStream.Write(by_send, 0, by_send.Length); //等待服務器端返回數據
        string recvStr = ""; byte[] recvBytes = new byte[4096]; int bytes = ntwStream.Read(recvBytes, 0, recvBytes.Length); //將接收到數據轉發輸出到Client編輯框
        recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); OutToClient(recvStr); //關閉連接, 一次發送完成
 ntwStream.Close(); NetworkClient.Close(); OutToClient("Conneting Close!\r\n"); } catch (ArgumentNullException ii) { OutToClient("ArgumentNullException: "+ ii); } catch (SocketException ii) { OutToClient("Socket failed! "); } catch (Exception ii) { OutToClient("Conneting faild!" + ii); } }

  服務器內的代碼涉及到的知識多一些

    1.通過按鍵1開辟監聽線程,用於接收

    2.監聽線程創建Socket套接字,綁定到指定端口並監聽

    3.監聽線程堵塞在等帶連接建立

    4.連接建立后監聽線程讀取客戶端數據並回送給客戶端處理后數據,然后循環堵塞在連接建立

    5.通過按鍵2刪除Socket套接字觸發異常,結束線程

    按照上述的流程,程序如下:

//新建線程 開始監聽
private void button1_Click(object sender, EventArgs e) { try { isConnectExit = false; button1.Enabled = false; button2.Enabled = true; Thread Listen_thread = new Thread(ListenClientConnect); Listen_thread.IsBackground = true; Listen_thread.Start(); } catch (Exception ii) { OutToServer("Connet faild!" + ii); } }
//監聽線程 主要處理數據接收發送
private void ListenClientConnect(object obj) { string s_ipaddress0 = textBox1.Text; string s_ipaddress1 = textBox2.Text; string s_ipaddress2 = textBox3.Text; string s_ipaddress3 = textBox4.Text; string s_port = textBox5.Text; //限定port的合法性
    bool isPort; int port; isPort = Int32.TryParse(s_port, out port); if (!(isPort && port >= 0 && port <= 65536)) { OutToServer("Port is Invalid!"); return; } //限定ip地址的合法性
    string host = s_ipaddress0 + "." + s_ipaddress1 + "." + s_ipaddress2 + "." + s_ipaddress3; if (!IsIpAdress(host)) { OutToServer("IpAdress is invaild!"); return; } IPAddress ip = IPAddress.Parse(host); IPEndPoint ipe = new IPEndPoint(ip, port); //新建socket通訊 綁定指定IP+Port 並監聽
    server_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); server_socket.Bind(ipe); server_socket.Listen(10); OutToServer("wait for connect..."); while (isConnectExit == false) { Socket CurrentSocket; try { //等待連接 完成三次握手
            CurrentSocket = server_socket.Accept(); if(isConnectExit == true) { CurrentSocket.Close(); // 關閉Socket
 server_socket.Close(); break; } OutToServer("Get a Connect!"); ServerProcess(CurrentSocket); } catch { OutToServer("Connet exti!"); } } }
//斷開連接
private void button2_Click(object sender, EventArgs e) { isConnectExit = true; button2.Enabled = false; button1.Enabled = true; if (server_socket != null) { server_socket.Close(); } }

如此就完成了簡單的C/S服務器功能。

實際測試界面如下:

具體源碼參考:C/S架構客戶端-服務器下載

備注:本程序僅用於演示TCP socket通信流程,服務器端能夠監聽的IP地址只有本機IP或者127.x.x.1(環回地址), 端口不能為80(和本機http端口沖突)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM