C#的Socket程式(TCP)

來源:互聯網
上載者:User

其實只要用到Socket聯結,基本上就得使用Thread,是交叉使用的。
C#封裝的Socket用法基本上不算很複雜,只是不知道託管之後的Socket有沒有其他效能或者安全上的問題。
在C#裡面能找到的最底層的操作也就是socket了,概念不做解釋。
程式模型如下:
WinForm程式 : 啟動連接埠偵聽;監視Socket聯結情況;定期關閉不活動的聯結;
Listener:處理Socket的Accept函數,偵聽新連結,建立新Thread來處理這些聯結(Connection)。
Connection:處理具體的每一個聯結的會話。

1:WinForm如何啟動一個新的線程來啟動Listener:
       //start the server
        private void btn_startServer_Click(object sender, EventArgs e)
        {
            //this.btn_startServer.Enabled = false;
            Thread _createServer = new Thread(new ThreadStart(WaitForConnect));
            _createServer.Start();
        }
        //wait all connections
        private void WaitForConnect()
        {
            SocketListener listener = new SocketListener(Convert.ToInt32(this.txt_port.Text));
             listener.StartListening();
        }
因為偵聽聯結是一個迴圈等待的函數,所以不可能在WinForm的線程裡面直接執行,不然Winform也就是無法繼續任何操作了,所以才指定一個新的線程來執行這個函數,啟動偵聽迴圈。
這一個新的線程是比較簡單的,基本上沒有啟動的參數,直接指定處理函數就可以了。
2:Listener如何啟動迴圈偵聽,並且啟動新的帶有參數的線程來處理Socket聯結會話。
先看如何建立偵聽:(StartListening函數)
IPEndPoint localEndPoint = new IPEndPoint(_ipAddress, _port);
        // Create a TCP/IP socket.
        Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            // Bind the socket to the local endpoint and  listen for incoming connections.
            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(20);//20 trucks

                // Start listening for connections.
                while (true)
                {
                   // here will be suspended while waiting for a new connection.
                    Socket connection = listener.Accept();
                    Logger.Log("Connect", connection.RemoteEndPoint.ToString());//log it, new connection
                ……
           }
         }……
基本步驟比較簡單:
建立原生IPEndPoint對象,表示以本機為伺服器,在指定連接埠偵聽;
然後綁定到一個偵聽Socket上;
進入while迴圈,等待新的聯結;
如果有新的聯結,那麼建立新的socket來對應這個聯結的會話。
   值得注意的就是這一句聯結代碼:listener.Accept()。執行這一句的時候,程式就在這個地方等待,直到有新的聯檢請求的時候程式才會執行下一句。這是同步執行,當然也可以非同步執行。
 
   新的聯結Socket建立了(Accept之後),對於這些新的socket該怎麼辦呢?他們依然是一個迴圈等待,所以依然需要建立新的Thread給這些Socket去處理會話(接收/發送訊息),而這個Thread就要接收參數了。
   Thread本身是不能接收參數的,為了讓它可以接收參數,可以採用定義新類,添加參數作為屬性的方法來解決。
   因為每一個Socket是一個Connection周期,所以我定義了這麼一個類public class Connection。這個類至少有這樣一個建構函式public Connection(Socket socket); 之所以這麼做,就是為了把Socket參數傳給這個Connection對象,然後好讓Listener啟動這個Thread的時候,Thread可以知道他正在處理哪一個Socket。
    具體處理的方法:(在Listener的StartListening函數,ocket connection = listener.Accept();之後)
    Connection gpsCn = new Connection(connection);
                    //each socket will be wait for data. keep the connection.
                    Thread thread = new Thread(new ThreadStart(gpsCn.WaitForSendData));
                    thread.Name = connection.RemoteEndPoint.ToString();
                    thread.Start();
 如此一來,這個新的socket在Accept之後就在新的Thread中運行了。
   3:Connection的會話處理
   建立了新的Connection(也就是socket),遠程就可以和這個socket進行會話了,無非就是send和receive。
   現在先看看怎麼寫的這個線程啟動並執行Connection. WaitForSendData函數
    while (true)
            {
                bytes = new byte[1024];
                string data = "";
                //systm will be waiting the msg of receive envet. like Accept();
                //here will be suspended while waiting for socket income msg.
                int bytesRec = this._connection.Receive(bytes);
                _lastConnectTime = DateTime.Now;
                if (bytesRec == 0)//close envent
                {
                    Logger.Log("Close Connection", _connection.RemoteEndPoint.ToString());
                    break;
                }
                data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                //…….handle your data.
             }
可以看到這個處理的基本步驟如下:
   執行Receive函數,接收遠程socket發送的資訊;
   把資訊從位元組轉換到string;
   處理該資訊,然後進入下一個迴圈,繼續等待socket發送新的資訊。
值得注意的有幾個:
   1:Receive函數。這個函數和Listener的Accept函數類似。在這個地方等待執行,如果沒有新的訊息,這個函數就不會執行下一句,一直等待。
   2:接收的是位元組流,需要轉化成字串
   3:判斷遠程關閉聯結的方式
   4:如果對方的訊息非常大,還得迴圈接收這個data。
4:如何管理這些聯結(thread)
通過上邊的程式,基本上可以建立一個偵聽,並且處理聯結會話。但是如何管理這些thread呢?不然大量產生thread可是一個災難。
管理的方法比較簡單,在Listener裡面我定義了一個靜態雜湊表(static public Hashtable Connections=new Hashtable();),儲存Connection執行個體和它對應的Thread執行個體。而connection中也加入了一個最後聯結時間的定義(private DateTime _lastConnectTime;)。在新連結建立的時候(Listener的Accept()之後)就把Connection執行個體和Thread執行個體存到雜湊表中;在Connection的Receive的時候修改最後聯結時間。這樣我們就可以知道該Connection在哪裡,並且會話是否活躍。
然後在Winform程式裡頭可以管理這些會話了,設定設定逾時。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.