TCP IP Socket In C, 2e - chapter 2 Basic TCP Socket

本章以TCP,即Stream Socket寫一個echo程序。

其實本章可以參考我的另一篇,就足夠了:Socke編程簡介
至於Socket的API函數可以參考:Linux下C Socket編程基礎API


1. IPv4 TCP Client

向Server發起通信請求,Server是被動等待連接的。

第一個程序,echo客戶端程序:TCPEchoClient4.c。這個程序還引入了另一個工具頭文件:Practical.h,其中使用到的函數實現在DieWithMessage.c。在兩個DieWithXxx函數中,爲什麼使用fputs(),而非printf(),是因爲把從網絡接收的數據作爲第一個參數傳遞給printf()有安全漏洞,至於是什麼安全漏洞我還不曉得,查了下有關printf格式化漏洞的,請參見*printf()格式化串安全漏洞分析(上)

客戶端工作步驟:

  1. 使用socket()創建一個TCP Socket;
  2. 使用connnect()向Server建立連接;
  3. 使用send()recv()進行通信;
  4. 使用close()關閉連接。

一般在基礎的Socket編程中使用的頭文件如下所列:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>

上述所說以及所用的函數原型可以通過Man Pages查到,查詢命令:man <functionName>


2. IPv4 TCP Server

C/S結構肯定是一對的,有客戶端和服務器端。很多系統都有一個echo服務器用來調試(debugging)和測試(testing),但是因爲安全的原因,這些服務器經常是不可用的。

這裏給出echo服務器端程序代碼:TCPEchoServer4.c。在這裏它還調用了Practical.h中另外一個函數HandleTCPClient(),不過該文件不止這一個函數,編譯的話,還需要和另外一個文件AddressUtility.c一起編譯,或者先註釋掉其它函數。

服務器端工作步驟:

  1. 使用socket()創建一個TCP socket;
  2. 使用bind()給socket分配一個端口號;
  3. 使用listen()告知系統允許使用上述端口的連接;
  4. 重複:
    • 對每一個客戶端連接調用accept()獲取一個新的socket
    • 使用send()recv()和客戶端進行通信;
    • 使用close()關閉客戶端連接。

看服務器端和客戶端的工作步驟就知道了,服務器端比客戶端多了bind()以及listen()accept()

2.1 設置socket

其實connect()也會綁定端口,不過用戶不在乎這個端口是哪個罷了,端口的分類與分配可以參考Service Name and Transport Protocol Port Number Registry

bind()這裏還有一個需要注意的,就是它使用了

servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // Any incoming interface

這個設置使得服務器可以不需要任何一個實質性的IP地址以接受客戶端連接。

bind()失敗的原因很多,一個主要原因是其它socket正在使用這個端口號以及一些端口號(如系統端口號1~1023)的使用需要權限。

listen()就是監聽bind()綁定的端口號,後一個參數表示,最多能夠處理的連接數量(參考listen()的註釋:N connection requests will be queued before further requests are refused.)

2.2 處理收到的連接

在這裏使用了無限循環。

accept()是一個阻塞方法,掛起等待直到客戶端發來連接請求,如果有了請求它會返回一個新的socket文件描述符,此時已經和遠端(客戶端)socket建立了連接。這樣就表示可以收發信息了,不過需要使用新的socket文件描述符。

2.3 注意事項

在編譯運行該程序的時候,需要注意,雖然客戶端程序port參數默認給的是7,但是實際上取決於你給服務器端設置的port參數。如果你要是把服務器端的端口設置爲7,就可能會出現在2.1節所說問題:bind() failed: Permission denied。所以一定要保證二者段口一致且別用系統端口。

Socket編程的一個重要原則是防衛式編程(Defensive Programming):就是說你的代碼不能信任從網絡上收到的任何信息!

作爲自己寫客戶端的替代,你可以使用telnet程序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章