connect()
客户端就是用connect()
函数来建立与服务器之间的连接的。
sockfd
是我们的老朋友了,是由socket()
函数返回的套接字描述符,serv_addr
是一个执行套接字地址结构 struct sockaddr
的指针, struct sockaddr
中存储了目标IP地址和端口号信息,addrlen
是serv_addr
指向的struct sockaddr
大小。
再次强调一下 getaddrinfo()
的妙处,所有你需要的连接信息都可以从这个函数的返回结果中获取。
客户端在调用connect()
之前不必非得调用bind()
函数,因为如果有必要,内核会确定源IP地址,并选择一个临时端口号作为源端口。
举一个使用connect()
连接到"www.chanmufeng.com" 的3490
端口的例子:
和上一节讲的bind()
一样,有些老代码在调用connect()
之前会手动封装 struct sockaddr_in
,然后传给connect()
作为参数。你现在仍然可以这么做,但是没必要!
如果你使用的是TCP套接字,调用connect
函数会触发TCP的三次握手过程,而且仅在连接成功或者失败时才会返回,失败返回-1
,并且会设置全局变量errno
的值。
listen()
之前讲的函数服务端和客户端都可以使用,让我们换换口味,这次讲一个仅用于服务端的函数,不仅如此,它还只能用于TCP服务端。
身为服务端,肯定是需要等待随时可能到来的客户端连接,并采用某种方式处理连接。整个过程可以分为两步:先调用listen()
,然后调用accept()
(这是下一小节的内容)。
listen
的调用非常简单,如下:
虽然只有两个参数,但是其中有一些细节值得玩味。
sockfd
就是通过socket()
返回的一个普通socket file descriptor
而已,系统默认这个socket只是一个等待进行主动连接的客户端socket。而listen
函数会把这个客户端socket转换为服务端socket,告诉内核需要接受指向该socket的连接请求,并且该socket的TCP状态从CLOSED
转换为LISTEN
。
backlog
参数表示内核应该为该套接字排队的最大连接个数。这是啥意思?所有客户端与该socket建立的连接都会在一个队列中排队等待,直到你accept()
(见下节)它们,这个参数就表示最多有多少个连接有资格排队。大多数系统将这个值预设为20,你可以初始化为5或者10。
还有老生常谈的,listen()
在错误的时候会返回 -1
,并设置 errno
。
联系之前讲过的3个系统调用,创建服务端代码需要调用的函数依次为:
我只是列出了函数的调用顺序而已,这几个步骤都比较简单,稍微需要点处理技巧的是下一节的accept()
。