客戶端 SOCKET 編程
建立客戶端的 Socket:
客戶端應用程序首先也是調用 WSAStartup() 函數來初始化 Winsock 的動態連接庫,然後同樣
調用 socket() 來建立一個 TCP 或 UDP Socket(相同協議的Socket 才能相遇,TCP 對 TCP,UDP 對 UDP)。
與服務器的 Socket 不同的是,客戶端的 Socket 可以調用 bind() 函數,來指定 IP 地址及端口號 port。
但也可以不調用 bind() 函數。而由 Winsock 來自動設定 IP 地址及端口號 port。
發起連接申請:
當客戶端程序需要連接服務器時,客戶端程序的 Socket 調用 connect() 函數監聽,與 name 所指定的計算機的特定
端口上的服務器端 Socket 進行連接。函數調用成功返回 0,否則返回 SOCKET_ERROR。
connect() 函數原型:
int connect( SOCKET s, // 客戶端流套接字 const struct sockaddr FAR *name, // 要連接的套接字的地址結構。 int namelen // 指明套接字的地址結構的長度。 );
函數示例:
//... struct sockaddr_in name; memset((void*)&name,0,sizeof(name)); name.sin_family= FA_INET; name.sin_port = htons(80); name.sin_addr.s_addr = inet_addr(""); connect(sSocket,(struct sockaddr *)&name,sizeof(name)); //...
inet_addr() 可以用來轉化字符串,主要用來將一個十進制的數轉化為二進制數,多用於 IPv4 的 ip 轉化。
返回:若字符串有效則將字符串轉換為32位二進制網絡字節序的IPV4地址,否則為INADDR_NONE。
用 Socket 實現數據的傳送:
服務器和客戶端分別創建 Socket 並連接後,就開始數據的傳遞。網絡編程數據傳送涉及兩大協議:TCP/IP 和 UDP 協議。
Stream (TCP) Socket:提供雙向,可靠,有次序,不重復的資料傳送。
Datagram (UDP) Socket:雖然提供雙向的通信,但是沒有 可靠,有次序,不重復的保證,
所以 UDP 傳送數據可能收到無次序,重復的資料,甚至資料在傳輸過程中會泄露。
一般情況下 TCP Socket 的數據發送和接收是調用 send() 和 recv() 這兩個函數來完成的,
而 UDP Socket 則是調用 sendto() 和 recvfrom() 這兩個函數,這兩個函數調用成功返回發送或接收的資料長度,
否則返回 SOCKET_ERROR。
send() 函數原型:
int send( SOCKET s, // 指定發送端套接字描述符。 const char FAR *buf, // 指明一個存在應用程序要發送數據的緩沖區。 int len, // 指明實際要發送的數據的字節數。 int flags // 一般置 0。 );
不論是客戶端還是服務端的應用程序都用 send() 函數向 TCP 連接的另一端發送數據。
客戶端一般用 send() 函數 向服務器發送請求,而服務器通常用 send() 函數來向客戶端程序發送應答。
對於同步 Socket 的 send() 函數執行流程,當調用它時,send 先比較待發送數據的長度(len)和套接字s 的發送端緩沖區大小。
如果 len 大於 s 的發送緩沖區的長度,該函數就返回 SOCKET_ERROR,發送失敗。
反之,那麽 send 先檢查協議是否正在發送 s 發送緩沖區的數據。如果是,就等待協議把數據發送完,
如果協議還沒有開始發送 s 的發送緩沖區的數據 或者 s 的發送緩沖區中沒有數據,
那麽send 就比較 s 的發送緩沖區的剩余空間和 len 的大小。如果 len 大於剩余空間大小,
send 就一直等待協議把 s 的發送緩沖區中的數據發送完,反之,send 就僅僅把 buf 中的數據復制到剩余空間裏
註意並不是 send 把 s 的發送緩沖區中的數據傳到連接的另一端的,而是協議傳的,
send 僅僅是把 buf 中的數據復制到 s 的發送緩沖區的剩余空間裏)。
如果 send() 函數復制數據成功,就返回實際復制的字節數,否則,就返回 SOCKET_ERROR。
如果 send() 函數在等待協議傳送數據時網絡斷開,也返回 SOCKET_ERROR。
send() 函數把 buf 中的數據成功復制到 s 的發送緩沖區的剩余空間中後它就返回了,但是此時這些數據
並不一定馬上就傳到連接的另一端。
如果協議在後續的傳遞過程中出現網絡錯誤的話,那麽下一個 socket 函數就會返回 SOCKET_ERROER。
提示:
每一個除 send 外的 socket 函數在執行最開始總要等待套接字的發送緩沖區中的數據被協議傳送完畢才能繼續,
如果出現網絡錯誤,那麽該 socket 函數就返回 SOCKET_ERROR。
recv() 函數原型:
int recv( SOCKET s, // 指定接收端套接字描述符。 char FAR *buf, // 指明一個緩沖區,該緩沖區用來存放 recv() 函數接收到的數據。 int len, // 指明 buf 的長度。 int flags // 一般置 0。 );
不論客戶端還是服務器端應用程序都用 recv() 函數從 TCP/IP 連接的另一端接收數據。
對於同步 Socket 的 recv() 函數執行流程,當調用它時,recv() 函數先等待 s 的發送緩沖區中的數據傳輸完畢。
如果協議在傳送 s 的發送緩沖區中的數據時出現網絡錯誤的話,那麽 recv() 函數就會返回 SOCKET_ERROER。
如果 s 的發送緩沖區中沒有數據 或者數據被協議接收完畢後,recv() 函數 會先檢查套接字 s 的接收緩沖區,
如果 s 的接收緩沖區中沒有數據或協議正在接收數據,那麽 recv() 函數就會一直等待,直到數據接收完畢。
當協議把數據接收完畢,recv() 函數就把 s 的接收緩沖區中的數據拷貝到 buf 中。
(註意協議接收到的數據可能大於 buf 的長度,所以在這種情況下需要調用幾次 recv() 函數才能把 s
接收緩沖區的數據拷貝完。recv() 函數僅僅是拷貝數據,真正的接收數據是協議完成的。)
recv() 函數返回其實是拷貝的字節數,如果 recv() 函數在拷貝時出錯,那麽它返回 SOCKET_ERROR。
如果 recv() 函數在等待協議接收數據時網絡中斷,那麽它返回 0。
sendto() 函數原型:
int sendto( SOCKET s, // 指定發送端套接字描述符。 const char *buf, // 指明一個存放應用程序要發送數據的緩沖區。 int len, // 指明實際要發送的數據字節數。 int flags, // 一般置 0。 const struct sockaddr* to, // 為指向目的地址的指針。(表示目的機的 ip 和 port). int tolen // 為 to 的長度。(一般賦值為 sizeof(sturct sockaddr). );
recvfrom() 函數原型:
int recvfrom( SOCKET s, // 指定接收端的套接字描述符。 char *buf, // 指明一個緩沖區,該緩沖區用來存放 recvfrom() 函數接收到的數據。 int len, // 指明 buf 的長度。 int flags, // 一般置 0. struct sockaddr *from, // 為數據包的來源地址。 int *fromlen // 數據包的長度。 );
客戶端 SOCKET 編程