[C# 網絡編程系列]專題五:TCP編程


前言

 

前面專題的例子都是基於應用層上的HTTP協議的介紹, 現在本專題來介紹下傳輸層協議——TCP協議,主要介紹下TCP協議的工作過程和基於TCP協議的一個簡單的通信程序,下面就開始本專題的正文了。

 

一、TCP的工作過程

首先TCP是一種面向連接的,可靠的,基於字節流的傳輸層通信協議。TCP的工作過程可以分為三個階段:一、連接的建立;  二、傳輸數據; 三、斷開連接,下面就對這三個過程分別介紹下:

1.1 連接的建立

TCP的連接建立就像打電話一樣, 我們打電話時,撥一個號碼的號碼並不是立即就可以接通的,期間會有一個“嘟 嘟”的呼叫過程, 這就好比是TCP協議的連接的建立階段。當我們用TCP編寫的程序,必須先建立TCP連接。TCP協議的連接建立通過三次握手來完成的,下面是在網上找的一張TCP三次握手的圖片:

 

下面就對這三次握手簡單的介紹:

第一次握手:建立連接時,客戶端發送SYN包(seq=x)到服務器,並進入SYN_Send狀態,等待服務器確認

第二次握手:服務器收到SYN包,必須確認客戶的SYN(ACK=x+1),同時自己也發送一個SYN包(SEQ=y),即SYN+ACK包,此時服務器進入SYN_Recv狀態

第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ACK=y+1),此包發送完畢,客戶端和服務器進入Established(建立)狀態,完成三次握手。

簡單理解三次握手就是發送一個檢驗包給對方然后互相確認,雙方都接到確認的一個信號時,這時候雙方就建立了連接(就像我們打電話時,如果沒人說話時就會說下 “喂”,說這句“喂” 也就是希望得到對方的一個確認,雖然這里雙方已經建立了連接的,這里只是更形象的說明下三次握手的過程)。

1.2 傳輸數據

雙方建立了連接,即在雙方建立了一個通信通道(就像一座橋一樣,在兩端建立了一個通路,用橋來比喻通信通道主要是因為最近有一則新聞:哈爾濱陽明灘大橋坍塌事件),建立連接之后,當然是傳輸我們需要傳輸的數據到對方的,這里就開始簡單介紹下傳輸數據的過程。

利用TCP傳輸數據時,數據是以字節流的形式進行傳輸,客戶端與服務器端建立連接后,發送方需要先將發送的數據轉換為字節流,然后將其發送給對方,發送數據時,可以通過程序不斷地將數據流陸續寫入TCP的發送緩沖中,然后TCP自動從發送緩沖中提取一定量的數據,將其組成TCP報文段發送到IP層,再通過IP層(也就是網絡層)之下的網絡接口發送出去;接受端從IP層接收到TCP報文段后,將其暫時保存在接受緩沖中,然后我們通過程序依次讀取接受緩沖中的數據,從而達到相互通信的目的(簡單的說就發送方把數據轉換為數據流,再把數據流存儲在發送緩沖中,然后傳輸層低層的協議從發送緩沖中讀取數據把數據發送出去,然后接收端從底層接受到數據把數據存儲在接收端的緩沖中,然后我們寫的程序只是從緩沖中依次讀取數據,然后顯示出來,在客戶端我們寫代碼做的事情是把數據寫入Write寫入發送端的緩沖中,然后服務器端(接收端)用Read方法在自己的緩沖中讀取數據,用一句話概括,TCP的傳輸就是對數據的寫——讀操作)括號中的內容只是我個人理解,因為這樣我感覺理解起來比較容易,對於剛開始接觸TCP的朋友可以這樣理解,然后再一句句話去擴展。

1.3 斷開連接

發送完數據之后,最后就是斷開連接了,下面是網上斷開的連接的一張圖片(斷開一個連接需要經過四次握手):

 

TCP的工作過程就分為上面三個過程,TCP編程是作為上層應用編程的基礎,就像之前專題中基於HTTP協議的Web服務器,Web瀏覽器,其傳輸層都用的是TCP協議進行傳輸的,還有基於FTP(文件傳輸協議),IMAP(交互式郵件存取協議) POP3(郵局協議的第3個版本) 和SMTP(簡單郵件傳輸協議)的網絡應用其傳輸層都用的是TCP協議,而不是UDP等其他傳輸層協議。

 

 

二、基於TCP協議的簡單通信程序

這里簡單實現了一個客戶端與服務器間的通信程序,核心代碼為:

客戶端連接服務器端代碼:

 private void btnConnect_Click(object sender, EventArgs e)
        {
            // 通過一個線程發起請求,多線程
            Thread connectThread = new Thread(ConnectToServer);
            connectThread.Start();
        }

        // 連接服務器方法,建立連接的過程
        private void ConnectToServer()
        {
            try
            {
                // 調用委托
                statusStripInfo.Invoke(showStatusCallBack, "正在連接...");
                if (tbxserverIp.Text == string.Empty || tbxPort.Text == string.Empty)
                {
                    MessageBox.Show("請先輸入服務器的IP地址和端口號");
                }

                IPAddress ipaddress = IPAddress.Parse(tbxserverIp.Text);
                tcpClient = new TcpClient();             
                tcpClient.Connect(ipaddress, int.Parse(tbxPort.Text));               
                
                // 延時操作
                Thread.Sleep(1000);
                if (tcpClient != null)
                {
                    statusStripInfo.Invoke(showStatusCallBack, "連接成功");
                    networkStream = tcpClient.GetStream();
                    reader = new BinaryReader(networkStream);
                    writer =new BinaryWriter(networkStream);
                }
                 

            }
            catch
            {
                statusStripInfo.Invoke(showStatusCallBack,"連接失敗");
                Thread.Sleep(1000);
                statusStripInfo.Invoke(showStatusCallBack,"就緒");
            }
        }

客戶端發送消息的代碼:

// 發送消息
        private void btnSend_Click(object sender, EventArgs e)
        {
            Thread sendThread = new Thread(SendMessage);
            sendThread.Start(tbxMessage.Text);
        }

        private void SendMessage(object state)
        {
            statusStripInfo.Invoke(showStatusCallBack, "正在發送...");
            try
            {
                writer.Write(state.ToString());
                Thread.Sleep(5000);
                writer.Flush();
                statusStripInfo.Invoke(showStatusCallBack, "完畢");

                tbxMessage.Invoke(resetMessageCallBack, null);
                lstbxMessageView.Invoke(showMessageCallback, state.ToString());
            }
            catch
            {
                if (reader != null)
                {
                    reader.Close();
                }
                if (writer != null)
                {
                    writer.Close();
                }
                if (tcpClient != null)
                {
                    tcpClient.Close();
                }

                statusStripInfo.Invoke(showStatusCallBack, "斷開了連接");
            }
        }

服務器端接受開始監聽客戶端請求的代碼:

 // 開始監聽
        private void btnStart_Click(object sender, EventArgs e)
        {
           
            tcpLister = new TcpListener(ipaddress,Port);
            tcpLister.Start();
            // 啟動一個線程來接受請求
            Thread acceptThread =new Thread(acceptClientConnect);
            acceptThread.Start();
        }

        // 接受請求
        private void acceptClientConnect()
        {
            statusStripInfo.Invoke(showStatusCallBack,"正在監聽");
            Thread.Sleep(1000);
            try
            {
                statusStripInfo.Invoke(showStatusCallBack,"等待連接");
                tcpClient = tcpLister.AcceptTcpClient();
                if (tcpLister != null)
                {
                    statusStripInfo.Invoke(showStatusCallBack,"接受到連接");
                    networkStream = tcpClient.GetStream();
                    reader = new BinaryReader(networkStream);
                    writer = new BinaryWriter(networkStream);
                }
            }
            catch
            {
                statusStripInfo.Invoke(showStatusCallBack, "停止監聽");
                Thread.Sleep(1000);
                statusStripInfo.Invoke(showStatusCallBack, "就緒");
            }
        }

現在看看運行的結果:
首先先啟動服務器然后點開始監聽,此時線程會堵塞,直到接受到一個連接請求位置

然后運行客戶端,在IP地址和端口處輸入服務器端的IP地址和端口號,點擊連接服務器按鈕后的界面如下:

通過接受按鈕和發送按鈕來實現雙方的通信,實現界面如下:

 

三、總結

到這里本專題的內容將的差不多了, 本專題主要介紹了基於TCP協議工作過程和在net平台下自定義了一個簡單通信的程序,希望本專題可以給那些初次接觸TCP協議的朋友一些幫助,(大牛們應該直接可以閃過的),在后面的專題我將和大家分享UDP編程,講完UDP編程后將結合這兩章的內容實現一個類似QQ的即時聊天的工具,希望這些對大家有幫助,如果大家有任何問題和有感興趣的專題需要了解的,可以給我留言,在之后的文章都會和大家來分享。

 

 

覺得看了后有幫助的朋友麻煩推薦下,也給我繼續下去的動力,如果大家有什么感興趣的專題也可以留言告訴我,我會通過學習后也會相繼和大家分享。

下面是本程序源代碼:

 http://files.cnblogs.com/zhili/%E7%AE%80%E5%8D%95%E9%80%9A%E4%BF%A1%E7%A8%8B%E5%BA%8F.zip

 


免責聲明!

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



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