connect()

客户端就是用​​connect()​​函数来建立与服务器之间的连接的。

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int;

​sockfd​​​是我们的老朋友了,是由​​socket()​​​函数返回的套接字描述符,​​serv_addr​​​是一个执行套接字地址结构 ​​struct sockaddr​​​ 的指针, ​​struct sockaddr​​​ 中存储了目标IP地址和端口号信息,​​addrlen​​​是​​serv_addr​​​指向的​​struct sockaddr​​ 大小。

再次强调一下 ​​getaddrinfo()​​ 的妙处,所有你需要的连接信息都可以从这个函数的返回结果中获取。

客户端在调用​​connect()​​​之前不必非得调用​​bind()​​函数,因为如果有必要,内核会确定源IP地址,并选择一个临时端口号作为源端口。

举一个使用​​connect()​​​连接到"​​www.chanmufeng.com​​​" 的​​3490​​端口的例子:

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo("www.example.com", "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// connect!

和上一节讲的​​bind()​​​一样,有些老代码在调用​​connect()​​​之前会手动封装 ​​struct sockaddr_in​​​,然后传给​​connect()​​作为参数。你现在仍然可以这么做,但是没必要!

如果你使用的是TCP套接字,调用​​connect​​​函数会触发TCP的三次握手过程,而且仅在连接成功或者失败时才会返回,失败返回​​-1​​​,并且会设置全局变量​​errno​​的值。

listen()

之前讲的函数服务端和客户端都可以使用,让我们换换口味,这次讲一个仅用于服务端的函数,不仅如此,它还只能用于TCP服务端。

身为服务端,肯定是需要等待随时可能到来的客户端连接,并采用某种方式处理连接。整个过程可以分为两步:先调用​​listen()​​​,然后调用​​accept()​​(这是下一小节的内容)。

​listen​​的调用非常简单,如下:

#include <sys/sock.h>

int listen(int sockfd, int;

虽然只有两个参数,但是其中有一些细节值得玩味。

​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个系统调用,创建服务端代码需要调用的函数依次为:

getaddrinfo();
socket();
bind();
listen();
/* accept() 将被写在这里 */

我只是列出了函数的调用顺序而已,这几个步骤都比较简单,稍微需要点处理技巧的是下一节的​​accept()​​。