【Linux 內核網絡協議棧源碼剖析】listen 函數剖析

listen 函數僅供服務器端調用,把一個未連接的套接字轉換爲一個被動套接字,指示內核應接受指向該套接字的連接請求。

1、應用層——listen 函數

#include <sys/socket.h>
int listen(int sockfd, int backlog);
/*sockfd是bind之後的套接口描述字,第二個參數規定了內核應該爲相應套接口排隊的最大連接個數*/
2、BSD Socket 層——sock_listen 函數

/*
 *	Perform a listen. Basically, we allow the protocol to do anything
 *	necessary for a listen, and if that works, we mark the socket as
 *	ready for listening.
 */
	//服務器端監聽客戶端的連接請求
//fd表示bind後的套接字文件描述符,backlog表示排隊的最大連接個數
//listen函數把一個未連接的套接字轉換爲一個被動套接字,
//指示內核應接受該套接字的連接請求
static int sock_listen(int fd, int backlog)
{
	struct socket *sock;

	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
		return(-EBADF);
	//給定文件描述符返回socket結構以及file結構指針,這裏file參數爲NULL,則表明對這個不感興趣
	if (!(sock = sockfd_lookup(fd, NULL))) 
		return(-ENOTSOCK);
    //前提是沒有建立連接,該套接字已經是連接狀態了,自然不需要
	if (sock->state != SS_UNCONNECTED) 
	{
		return(-EINVAL);
	}
	//調用底層實現函數(inet_listen)
	if (sock->ops && sock->ops->listen)
		sock->ops->listen(sock, backlog);
	sock->flags |= SO_ACCEPTCON;//設置標識字段,表示正在監聽
	return(0);
}
3、INET Socket 層——inet_listen 函數

/*
 *	Move a socket into listening state.
 */
 //sock_listen的下層調用函數
 //這個函數主要是對sock結構中state字段的設置。listen函數到這層完成處理
static int inet_listen(struct socket *sock, int backlog)
{
	//獲取sock數據結構
	struct sock *sk = (struct sock *) sock->data;
    //如果sock的端口號爲0(未綁定任何端口號),則自動綁定一個本地端口號(新的未使用的最小的端口號)
    //如果事先已經綁定了一個端口號,那麼這個代碼將不會執行
	if(inet_autobind(sk)!=0)
		return -EAGAIN;

	/* We might as well re use these. */ 
	/*
	 * note that the backlog is "unsigned char", so truncate it
	 * somewhere. We might as well truncate it to what everybody
	 * else does..
	 */
	 //等待的最大數.內核限制最大連接數是5
	if ((unsigned) backlog > 5)
		backlog = 5;
	sk->max_ack_backlog = backlog;//緩存的最大未應答數據包個數
	if (sk->state != TCP_LISTEN)//如果不是listen狀態,則置位listen狀態
	{
		sk->ack_backlog = 0;//緩存的未應答數據包個數清0
		sk->state = TCP_LISTEN;
	}
	return(0);
}
可以看出 inet_listen 函數主要就是設置 sock 的狀態爲TCP_LISTEN。tcp的三次握手以及四次揮手就是基於這樣的一些狀態。

函數內部有調用 inet_autobind 函數,該函數是爲沒有綁定端口的sock結構自動綁定一個端口號(系統可用的最小端口號)

/*
 *	Automatically bind an unbound socket.
 */
 //自動綁定一個本地端口號,一般用於客戶端,實際應用層編程時,對於客戶端我們並沒有
 //特別去綁定某個端口號,而是由系統自動綁定
static int inet_autobind(struct sock *sk)
{
	/* We may need to bind the socket. */
	if (sk->num == 0) 
	{
	//獲取一個新的未使用的端口號
		sk->num = get_new_socknum(sk->prot, 0);
		if (sk->num == 0) 
			return(-EAGAIN);
		put_sock(sk->num, sk);//加入到sock_array哈希表中
		//將一個無符號短整型數從網絡字節順序轉換爲主機字節順序。大小端問題
		sk->dummy_th.source = ntohs(sk->num);//TCP首部中的source字段表示本地端口號
	}
	return 0;
}
listen 函數把一個未連接的套接字轉換爲一個被動套接字,指示內核應接受指向該套接字的連接請求,其內部實現歸根到底就是設置 sock 結構的狀態,設置其爲 TCP_LISTEN。功能很簡單。
這個函數也是服務器端調用,其套接字的地址信息狀態和bind函數執行之後是一樣的,只綁定了本地地址信息,不知道對端的地址信息。


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