4.1 理解TCP和UDP
TCP是TransmissionControl Protocol(傳輸控制協議)的簡寫。
以多個標準爲依據設計的系統稱爲開放式系統。
TCP和UDP層以IP層提供的路徑信息爲基礎完成實際的數據傳輸,故該層又稱傳輸層(Transport)。
IP層是面向消息的、不可靠的協議。只關注1個數據包(數據傳輸的基本單位)的傳輸過程。若只利用IP層傳輸數據,則有可能導致數據丟失或者數據包收、發順序不一致。TCP和UDP存在於IP層之上,決定主機之間的數據傳輸方式。
編寫軟件的過程中,需要根據程序特點決定服務器和客戶端之間的數據傳輸規則(規定),這便是應用層協議。網絡編程的大部分內容就是設計並實現應用層協議。
4.2 實現基於TCP的服務器端/客戶端
1. TCP端的默認函數調用順序
進入等待連接請求狀態:
#include <sys/socket.h>
int listen(int sock, int backlog);
-sock: 希望進入等待連接請求狀態的套接字文件描述符,傳遞的描述符套接字參數成爲服務器端套接字(監聽套接字);
-backlog: 連接請求等待隊列(Queue)的長度,若爲5,則隊列長度爲5,表示最多使5個連接請求進入隊列;連接請求隊列的大小始終根據實驗結果而定。
受理客戶端連接請求:
#include<sys/socket.h>
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
-sock: 服務器套接字的文件描述符;
-addr: 保存發起連接的客戶端地址信息的變量地址值,調用函數後向傳遞來的地址變量參數填充客戶端地址信息;
-addrlen: 第二個參數addr的長度,注意是該參數是指針變量,應傳入長度變量的地址;
accept函數受理連接請求等待隊列中待處理的客戶端連接請求。函數調用成功後,accept函數內部將產生用於數據I/O的套接字,並返回其文件描述符。
另外,調用accept函數時,若等待隊列爲空,則accept函數不會返回,直到隊列中出現新的客戶端連接。
2. TCP客戶端的默認函數調用順序
與服務器端相比,區別就在於“請求連接”,服務器端調用listen函數後創建連接請求等待隊列,之後客戶端即可請求連接。
#include <sys/socket.h>
int connect(int sock, struct sockaddr*servaddr, socklen_t addrlen);
-sock: 客戶端套接字文件描述符;
-servaddr: 保存目標服務器端地址信息的變量地址值;
-addrlen: 以字節爲單位傳遞已傳遞給第二個結構體參數servaddr的地址變量長度;
客戶端調用connect函數後,發生以下情況之一纔會返回:
A.服務器端接收連接請求(服務器端將請求消息記錄到等待隊列),因此,connect函數返回後並不立即進行數據交換;
B.發生斷網等異常情況而中斷連接請求;
網絡數據交換必須分配IP和端口,客戶端的IP地址和端口在調用connect函數時自動分配(操作系統,內核),端口號隨機分配,無需調用標記的bind函數進行分配。
3. 基於TCP的服務器端/客戶端函數調用關係
4.3 實現迭代服務器端/客戶端
1. 迭代回聲服務器端/客戶端
程序運行方式:
服務器端在同一時刻只與一個客戶端相連,並提供回聲服務;
服務器端依次爲5個客戶端連接提供服務並退出;
客戶端接收用戶輸入的字符串併發送到服務器端;
服務器端將接收的字符串數據傳回客戶端,即“回聲”;、
服務器端與客戶端之間的字符串回聲一直執行到客戶端輸入Q爲止;
2. 回聲客戶端存在的問題
多次調用write函數傳遞的字符串有可能一次性傳遞到服務器端,此時客戶端有可能從服務器端收到多個字符串,這不是希望的結果。