C#網(wǎng)絡(luò)編程系列五:TCP編程
前面專題的例子都是基于應(yīng)用層上的HTTP協(xié)議的介紹, 現(xiàn)在本專題來介紹下傳輸層協(xié)議——TCP協(xié)議,主要介紹下TCP協(xié)議的工作過程和基于TCP協(xié)議的一個簡單的通信程序,下面就開始本專題的正文了。
一、TCP的工作過程
首先TCP是一種面向連接的,可靠的,基于字節(jié)流的傳輸層通信協(xié)議。TCP的工作過程可以分為三個階段:一、連接的建立; 二、傳輸數(shù)據(jù); 三、斷開連接,下面就對這三個過程分別介紹下:
1.1 連接的建立
TCP的連接建立就像打電話一樣, 我們打電話時,撥一個號碼的號碼并不是立即就可以接通的,期間會有一個“嘟 嘟”的呼叫過程, 這就好比是TCP協(xié)議的連接的建立階段。當(dāng)我們用TCP編寫的程序,必須先建立TCP連接。TCP協(xié)議的連接建立通過三次握手來完成的,下面是在網(wǎng)上找的一張TCP三次握手的圖片:
下面就對這三次握手簡單的介紹:
第一次握手:建立連接時,客戶端發(fā)送SYN包(seq=x)到服務(wù)器,并進(jìn)入SYN_Send狀態(tài),等待服務(wù)器確認(rèn)
第二次握手:服務(wù)器收到SYN包,必須確認(rèn)客戶的SYN(ACK=x+1),同時自己也發(fā)送一個SYN包(SEQ=y),即SYN+ACK包,此時服務(wù)器進(jìn)入SYN_Recv狀態(tài)
第三次握手:客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ACK=y+1),此包發(fā)送完畢,客戶端和服務(wù)器進(jìn)入Established(建立)狀態(tài),完成三次握手。
簡單理解三次握手就是發(fā)送一個檢驗(yàn)包給對方然后互相確認(rèn),雙方都接到確認(rèn)的一個信號時,這時候雙方就建立了連接(就像我們打電話時,如果沒人說話時就會說下 “喂”,說這句“喂” 也就是希望得到對方的一個確認(rèn),雖然這里雙方已經(jīng)建立了連接的,這里只是更形象的說明下三次握手的過程)。
1.2 傳輸數(shù)據(jù)
雙方建立了連接,即在雙方建立了一個通信通道(就像一座橋一樣,在兩端建立了一個通路,用橋來比喻通信通道主要是因?yàn)樽罱幸粍t新聞:哈爾濱陽明灘大橋坍塌事件),建立連接之后,當(dāng)然是傳輸我們需要傳輸?shù)臄?shù)據(jù)到對方的,這里就開始簡單介紹下傳輸數(shù)據(jù)的過程。
利用TCP傳輸數(shù)據(jù)時,數(shù)據(jù)是以字節(jié)流的形式進(jìn)行傳輸,客戶端與服務(wù)器端建立連接后,發(fā)送方需要先將發(fā)送的數(shù)據(jù)轉(zhuǎn)換為字節(jié)流,然后將其發(fā)送給對方,發(fā)送數(shù)據(jù)時,可以通過程序不斷地將數(shù)據(jù)流陸續(xù)寫入TCP的發(fā)送緩沖中,然后TCP自動從發(fā)送緩沖中提取一定量的數(shù)據(jù),將其組成TCP報文段發(fā)送到IP層,再通過IP層(也就是網(wǎng)絡(luò)層)之下的網(wǎng)絡(luò)接口發(fā)送出去;接受端從IP層接收到TCP報文段后,將其暫時保存在接受緩沖中,然后我們通過程序依次讀取接受緩沖中的數(shù)據(jù),從而達(dá)到相互通信的目的(簡單的說就發(fā)送方把數(shù)據(jù)轉(zhuǎn)換為數(shù)據(jù)流,再把數(shù)據(jù)流存儲在發(fā)送緩沖中,然后傳輸層低層的協(xié)議從發(fā)送緩沖中讀取數(shù)據(jù)把數(shù)據(jù)發(fā)送出去,然后接收端從底層接受到數(shù)據(jù)把數(shù)據(jù)存儲在接收端的緩沖中,然后我們寫的程序只是從緩沖中依次讀取數(shù)據(jù),然后顯示出來,在客戶端我們寫代碼做的事情是把數(shù)據(jù)寫入Write寫入發(fā)送端的緩沖中,然后服務(wù)器端(接收端)用Read方法在自己的緩沖中讀取數(shù)據(jù),用一句話概括,TCP的傳輸就是對數(shù)據(jù)的寫——讀操作)括號中的內(nèi)容只是我個人理解,因?yàn)檫@樣我感覺理解起來比較容易,對于剛開始接觸TCP的朋友可以這樣理解,然后再一句句話去擴(kuò)展。
1.3 斷開連接
發(fā)送完數(shù)據(jù)之后,最后就是斷開連接了,下面是網(wǎng)上斷開的連接的一張圖片(斷開一個連接需要經(jīng)過四次握手)
TCP的工作過程就分為上面三個過程,TCP編程是作為上層應(yīng)用編程的基礎(chǔ),就像之前專題中基于HTTP協(xié)議的Web服務(wù)器,Web瀏覽器,其傳輸層都用的是TCP協(xié)議進(jìn)行傳輸?shù)?,還有基于FTP(文件傳輸協(xié)議),IMAP(交互式郵件存取協(xié)議) POP3(郵局協(xié)議的第3個版本) 和SMTP(簡單郵件傳輸協(xié)議)的網(wǎng)絡(luò)應(yīng)用其傳輸層都用的是TCP協(xié)議,而不是UDP等其他傳輸層協(xié)議。
二、基于TCP協(xié)議的簡單通信程序
這里簡單實(shí)現(xiàn)了一個客戶端與服務(wù)器間的通信程序,核心代碼為:
客戶端連接服務(wù)器端代碼:
- private void btnConnect_Click(object sender, EventArgs e)
- {
- // 通過一個線程發(fā)起請求,多線程
- Thread connectThread = new Thread(ConnectToServer);
- connectThread.Start();
- }
- // 連接服務(wù)器方法,建立連接的過程
- private void ConnectToServer()
- {
- try
- {
- // 調(diào)用委托
- statusStripInfo.Invoke(showStatusCallBack, "正在連接...");
- if (tbxserverIp.Text == string.Empty || tbxPort.Text == string.Empty)
- {
- MessageBox.Show("請先輸入服務(wù)器的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,"就緒");
- }
- }
客戶端發(fā)送消息的代碼:
- // 發(fā)送消息
- 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, "正在發(fā)送...");
- 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, "斷開了連接");
- }
- }
服務(wù)器端接受開始監(jiān)聽客戶端請求的代碼:
- // 開始監(jiān)聽
- 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,"正在監(jiān)聽");
- 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, "停止監(jiān)聽");
- Thread.Sleep(1000);
- statusStripInfo.Invoke(showStatusCallBack, "就緒");
- }
- }
現(xiàn)在看看運(yùn)行的結(jié)果:
首先先啟動服務(wù)器然后點(diǎn)開始監(jiān)聽,此時線程會堵塞,直到接受到一個連接請求位置
然后運(yùn)行客戶端,在IP地址和端口處輸入服務(wù)器端的IP地址和端口號,點(diǎn)擊連接服務(wù)器按鈕后的界面如下:
通過接受按鈕和發(fā)送按鈕來實(shí)現(xiàn)雙方的通信,實(shí)現(xiàn)界面如下:
三、總結(jié)
到這里本專題的內(nèi)容將的差不多了, 本專題主要介紹了基于TCP協(xié)議工作過程和在net平臺下自定義了一個簡單通信的程序,希望本專題可以給那些初次接觸TCP協(xié)議的朋友一些幫助,(大牛們應(yīng)該直接可以閃過的),在后面的專題我將和大家分享UDP編程,講完UDP編程后將結(jié)合這兩章的內(nèi)容實(shí)現(xiàn)一個類似QQ的即時聊天的工具,希望這些對大家有幫助,如果大家有任何問題和有感興趣的專題需要了解的,可以給我留言,在之后的文章都會和大家來分享。
覺得看了后有幫助的朋友麻煩推薦下,也給我繼續(xù)下去的動力,如果大家有什么感興趣的專題也可以留言告訴我,我會通過學(xué)習(xí)后也會相繼和大家分享。
下面是本程序源代碼:
http://files.cnblogs.com/zhili/%E7%AE%80%E5%8D%95%E9%80%9A%E4%BF%A1%E7%A8%8B%E5%BA%8F.zip
原文鏈接:http://www.cnblogs.com/zhili/archive/2012/08/25/TCP.html
【編輯推薦】
- C#網(wǎng)絡(luò)編程系列一:網(wǎng)絡(luò)協(xié)議簡介
- C#網(wǎng)絡(luò)編程系列二:HTTP協(xié)議詳解
- C#網(wǎng)絡(luò)編程系列三:自定義Web服務(wù)器
- C#網(wǎng)絡(luò)編程系列四:自定義Web瀏覽器
- C#網(wǎng)絡(luò)編程系列六:UDP編程
- C#網(wǎng)絡(luò)編程系列七:UDP編程補(bǔ)充
- C#網(wǎng)絡(luò)編程系列八:P2P編程
- C#網(wǎng)絡(luò)編程系列九:類似QQ的即時通信程序
- C#網(wǎng)絡(luò)編程系列十:實(shí)現(xiàn)簡單的郵件收發(fā)器