這兩天一直在看《linux C編程實戰》網路編程一章,主要研究的是套接字編程這部分。裏面的大部分程序自己都上機驗證了。最後的一個綜合應用,服務器/客戶端 程序自己也是親自敲進電腦的。也許敲的過程就是一種學習,可以發現裏面一些細節上的問題,這是光看發現不了的。根據這本書的講解,再依據對最後這個應用程序的理解,自己把套接字編程的需要注意的地方總結下。
套接字地址結構
結構struct sockaddr 定義了一種通用的套接字地址,它的類型是:
struct sockaddr
{
unsigned short sa_family; //地址類型,一般爲AF_INET
char sa_data[14]; //14字節的協議地址
};
這是一種通用的定義,一般都不用。TCP/IP使用的是自己的結構體struct sockaddr_in,格式如下:
struct sockaddr_in
{
unsigned short sin_family; //地址類型,一般爲AF_INET
unsigned short sin_port; //端口號
struct in_addr sin_addr; //IP地址
unsigned char sin_zero[8]; //填充字節,一般賦值爲0。爲了使結構體長度達到16字節,匹配結構體sockaddr
};
這裏的struct in_addr的定義如下:
struct in_addr
{
unsigned long s_addr;
};
結構體sockaddr和sockaddr_in的長度都是16字節。一般在編TCP/IP程序時,一般使用結構體sockaddr_in來設置地址,然後在需要的時候,通過強制類型轉換成sockaddr類型。
創建套接字
socket函數用來創建一個套接字,函數原型:
int socket(int domain, int type, int protocol);
參數domain指定創建套接字所使用的協議族;
參數type指定套接字的類型;
參數protocol通常設置爲0。
函數執行成功,返回一個新創建的套接字,錯誤返回-1。
建立連接
函數connect用來在一個指定的套接字上創建一個連接,函數原型:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
參數sockfd是一個由函數socket創建的套接字;
參數serv_addr是一個地址結構,需要連接的地址;
參數addrlen爲參數addr_addr的長度。
函數執行成功返回0,有錯誤發生則返回-1。
如果套接字類型是TCP,則該函數用於向服務器發出連接請求,服務器的IP地址和端口號由參數serv_addr指定;如果套接字類型是UDP,則該函數並不建立真正的連接,它只是告訴內核與該套接字進行通信的目的地址(由第二個參數指定),只有該目的地址發來的數據纔會被該socket接收。
通常一個面向連接的套接字只能調用一次connect函數;而對於無連接的套接字則可以多次調用connect函數以改變與目的地址的綁定。
在套接字上監聽
函數listen把套接字轉化爲被動監聽,函數原型:
int listen(int s, int backlog);
參數s指定了一個套接字;
參數backlog指定了該連接隊列的最大長度,如果已達到最大,則之後的連接請求將被服務器拒絕。
函數執行成功趕回0,有錯誤發生則返回-1。
由函數socket創建的套接字是主動套接字,這種套接字可以用來主動請求連接到某個服務器上。(通過connect()函數)。
作爲服務器端的程序,通常在某個端口上監聽等待來自客戶端的連接請求。在服務器端,一般是先調用函數socket創建一個主動套接字,然後調用函數bind將該套接字綁定到某個端口上,接着再調用函數listen將該套接字轉化爲監聽套接字,等待來自於客戶端的連接請求。
函數listen只是將套接字設置爲傾聽模式以等待連接請求,它並不能接收連接請求,真正的接收客戶端連接請求的是accept()函數。
接收連接
函數accept用來接收一個連接請求,函數原型:
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
參數s是由socket創建,經函數bind綁定到本地某一端口上,然後通過函數listen轉化而來的監聽套接字;
參數addr用來保存發起連接請求的主機的地址和端口;
參數addrlen是addr所指向的結構體的大小。
函數執行成功返回一個新的代表客戶端的套接字,出錯則返回-1。
只能對面向連接的套接字使用accept函數。accept執行成功時,將創建一個新的套接字,並且這個新的套接字分配一個套接字描述符,並返回這個新的套接字描述符。這個新的套接字描述符與打開文件返回的文件描述符類似,進程可以利用這個新的套接字描述符與客戶端交換數據,參數s所指定的套接字繼續等待客戶端的連接請求。