listen函數
函數原型
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能
將套接字文件描述符,從主動文件描述符變爲被動描述符,然後用於被動監聽客戶的連接。
返回值
成功返回0,失敗返回-1,ernno被設置
參數
sockfd
socket所返回的套接字文件描述符。
socket返回的“套接字文件描述符”默認是主動的,如果你想讓它變爲被動的話,你需要自己調用listen函數來實現。
backlog
指定隊列的容量。
這個隊列用於記錄正在連接,但是還沒有連接完成的客戶,一般將隊列容量指定爲2、3就可以了。
這個容量並沒有什麼統一個設定值,一般來說只要小於30即可。
代碼演示
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SPROT 5006
#define SIP "192.168.31.162"
void print_err(char * str,int line,int err_no)//出錯處理函數
{
printf("%d,%s: %s\n",line,str,strerror(err_no));
exit(-1);
}
int main(void)
{
int ret = -1;
int sockfd = -1; //存放套接字文件描述符
/*創建使用TCP協議通信的套接字文件*/
sockfd = socket(PF_INET,SOCK_STREAM,0); //指定TCP協議
if(-1 == sockfd) //進行出錯處理
print_err("socket fail",__LINE__,errno);
/*調用bind函數綁定套接字文件/ip/端口*/
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;//指定ip格式
saddr.sin_port= htons(SPROT);//指定端口
saddr.sin_addr.s_addr = inet_addr(SIP);//設置ip
ret = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//綁定
if(-1 ==ret)
print_err("bind fail",__LINE__,errno);
/*將主動的套接字文件描述符轉換爲被動的文件描述符,用於被動監聽客戶連接。*/
ret = listen(sockfd,3);
if(-1 ==ret)
print_err("listen fail",__LINE__,errno);
return 0;
}
實現被動監聽客戶端連接使用的是另外一個函數,listen函數本身並不會去監聽,只是把主動描述符轉化爲被動描述符,真正等待監聽的函數時另外的函數。
不要因爲listen有聽的意思,就想當然的認爲,listen就是用於被動監聽客戶連接的函數,事實上真正用於被動監聽客戶連接的函數,並不是listen,而是其它函數。
listen的作用僅僅只是用於將“套接字文件描述符”變成被動描述符,以供“監聽函數”用於被動監聽客戶連接而已。
TCP服務器爲什麼要listen
一定要注意 : 只有TCP服務器纔會調用listen
圖解說明:TCP編程模型
TCP服務器調用listen函數的目的。
TCP服務器監聽客戶連接時,是使用socket返回的“套接字文件描述符”來實現的,但是這個文件描述符默認是主動文件描述符,所以需要使用listen函數將其轉爲被動描述符,否者無法用於被動監聽客戶的連接。
什麼是主動描述符?
使用主動描述符可以主動的向對方發送數據。
什麼是被動描述符?
只能被動的等別人主動想你發數據,然後再回答數據,不能主動的發送數據。
爲什麼要將“套接字文件描述符”轉爲被動描述符後,才能監聽連接?
TCP服務器和客戶端必須要建立連接,建立連接時的三次握手是由客戶端主動先發起的,
也就是由客戶端率先主動向服務器發送三次握手的數據,而服務器這一端則被動的接收,然後回答。
圖解說明:
正是由於服務器在三次握手時的這一被動屬性,所以使用“套接字文件描述符”監聽客戶連接時,描述符必須是被動的描述符,否則服務器無法監聽被動連接。