這節我們看一下服務器端的socket監聽,還是從應用程序界面看起
listen(server_sockfd, 5);
|
中間的過程我們不說了,只說到達了函數sys_listen()處,不知道這個過程的朋友參考前面幾節內容。這個函數在/net/socket.c中的1370行處。
asmlinkage long sys_listen(int fd, int backlog) { struct socket *sock; int err, fput_needed; int somaxconn;
sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn; if ((unsigned)backlog > somaxconn) backlog = somaxconn;
err = security_socket_listen(sock, backlog); if (!err) err = sock->ops->listen(sock, backlog);
fput_light(sock->file, fput_needed); } return err; }
|
sockfd_lookup_light()函數我們在前邊看到過了,我們同時比照一下sys_listen()與sys_bind()函數的差別,發現基本相同,只不同的地方就是
sock->ops->listen(sock, backlog);
|
這裏可以看一下我們以前提到的unix的二個結構
static const struct proto_ops unix_stream_ops
|
static const struct proto_ops unix_dgram_ops
|
這裏我們的socket是使用的第一個unix_stream_ops即tcp有連接的socket,可以看到這個結構中
因此我們上面的sys_listen()會執行unix_listen這個鉤子函數,我們看一下,這個函數在/net/unix/Af_unix.c中的451行處
static int unix_listen(struct socket *sock, int backlog) { int err; struct sock *sk = sock->sk; struct unix_sock *u = unix_sk(sk);
err = -EOPNOTSUPP; if (sock->type!=SOCK_STREAM && sock->type!=SOCK_SEQPACKET) goto out; /* Only stream/seqpacket sockets accept */ err = -EINVAL; if (!u->addr) goto out; /* No listens on an unbound socket */ unix_state_lock(sk); if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) goto out_unlock; if (backlog > sk->sk_max_ack_backlog) wake_up_interruptible_all(&u->peer_wait); sk->sk_max_ack_backlog = backlog; sk->sk_state = TCP_LISTEN; /* set credentials so connect can copy them */ sk->sk_peercred.pid = task_tgid_vnr(current); sk->sk_peercred.uid = current->euid; sk->sk_peercred.gid = current->egid; err = 0;
out_unlock: unix_state_unlock(sk); out: return err; }
|
從上文的註釋中我們也可以看出,這裏只支持有連接的socket,並且我們也可以看出上邊有一個sk_state用來判斷sock的狀態,同時我們也看到if (!u->addr)沒有綁定地址的socket不允許監聽,接下來,判斷一下要監聽的隊列個數,這是從我們的應用程序傳遞下來的5個連接,所以如果這個鏈接大於sock的最大連接數就要喚醒在socket中睡眠等待連接的進程,讓他們有機會與服務器端的socket連接成功。我們以前在練習中看到,listen()界面是用在服務器端的socket一側的,所以,這裏將服務器端的進程號以及狀態設置爲TCP_LISTEN,將來客戶端在connect()請求連接並經服務器端的accept()同意連接後,就會獲得這裏的服務器端的sock中關於服務進程的信息。