Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1798381
  • 博文数量: 438
  • 博客积分: 9799
  • 博客等级: 中将
  • 技术积分: 6092
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 17:25
文章分类

全部博文(438)

文章存档

2019年(1)

2013年(8)

2012年(429)

分类: 系统运维

2012-04-03 09:48:53

如果我们处理一个面向连接的网络服务(SOCK_STREAM或SOCK_SEQPACKET),那么在我们交换数据前,我们需要在请求服务的进程(客户)的套接字和提供服务的进程(服务器)的套接字之间创建一个连接。我们使用connect函数来创建一个连接。



  1. #include <sys/socket.h>

  2. int connect(int sockfd, const struct sockaddr *addr, socklen_t len);

  3. 成功返回0,错误返回-1


我们在connect里指定的地址是我们想通信的服务器的地址。如果sockfd没有绑定到一个地址,connect会为调用者创建一个默认的地址。


当 我们尝试连接一个服务器时,连接请求可能因为几种原因失败。我们尝试连接的机器必须开启且正运行,服务器必须绑定到我们尝试联系的地址,且在服务器的待定 连接队列里必须有空间(我们马上会学习更多关于这个的东西)。因而,应用必须能够处理由临时情况导致的connect错误返回。


下面的代码展示一种处理临时connect错误的方法。这很可能是正运行在一个高负载系统上的一个服务器。



  1. #include <sys/socket.h>

  2. #define MAXSLEEP 128

  3. int
  4. connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
  5. {
  6.     int nsec;

  7.     /*
  8.      * Try to connect with exponential backoff.
  9.      */
  10.     for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) {
  11.         if (connect(sockfd, addr, alen) == 0) {
  12.             /*
  13.              * Connection accepted.
  14.              */
  15.             return(0);
  16.         }
  17.         
  18.         /*
  19.          * Delay before trying again.
  20.          */
  21.         if (nsec <= MAXSLEEP/2)
  22.             sleep(nsec);
  23.     }
  24.     return(-1);
  25. }

这个函数展示了被称为指数退避算法。如果connect的调用失败,进程睡一会然后再次尝试,每次循环都增加延迟,最大的延迟大约为2分钟。

如果套接字描述符是非阻塞模式,16.8节深入讨论,那么connect将返回-1并设置errno为特殊的错误码EINPROGRESS,如果连接不能被立即建立。应用可以使用poll或select来确定文件描述符何是可用。在这时,连接完成。


connect 函数也可以用在一个无连接的网络服务上(SOCK_DGRAM)。这可能像一个缩减,但其实是一个优化。如果我们用一个SOCK_DGRAM套接字调用 connect,那么我们发送的所有消息的目的地被设置为我们在connect调用里指定的地址,使我们解脱不必每次传送一个消息时都要提供地址。此外, 我们将只从我们指定的地址收到数据报。


一个服务器声明它想要接受连接请求,通过调用listen函数。



  1. #include <sys/socket.h>

  2. int listen(int sockfd, int backlog);

  3. 成功返回0,错误返回-1。


backlog参数提供一个提示给系统,关于它应该代理进程排队的显著连接请求的数量。真实值由系统决定,但上限作为SOMAXCONN定义在


在Solaris上,里的SOMAXCONN值被忽略。特定的值取决于每个协议的实现。对于TCP,默认为128。


一旦队列满了,系统将拒绝额外的连接请求,所以backlog值必须基于服务器期望的负载和它必须执行的用来接受一个连接请求并启动服务的处理的所需的时间来选择。


一旦服务器调用listen,使用的套接字可以接收连接请求。我们使用accept函数来得到一个连接请求,并把它转换到一个连接。



  1. #include <sys/socket.h>

  2. int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);

  3. 成功返回文件(套接字)描述符,错误返回-1。


accept返回的文件描述符是一个套接字描述符,它被连接到调用connect的客户。这个新的套接字描述符和原始套接字(sockfd)有相同的套接字类型和地址族。传递给accept的原始套接字和这个连接没有关联,但是仍然保持可用以接收额外的连接请求。


如 果我们不关心客户的身份,那么我们可以设置addr和len参数为NULL。否则,在调用accept之前,我们需要设置addr参数为一个足够大的缓冲 以容纳地址,并调用len指向的整型为缓冲的尺寸。在返回时,accept将在缓冲里填充客户的地址并更新len指向的整型来返回地址的尺寸。


如果没有连接请求待定,那么accept将阻塞,直到某个请求到达。如果sockfd是非阻塞模式,那么accept将返回-1并设置errno为EAGAIN或EWOULDBLOCK。


本文四个平台都把EAGAIN定义为和EWOULDBLOCK相同的值。


如是一个服务器调用accept且没有连接请求出现,那么服务器将阻塞,直到某个到达。可替代地,一个服务器可以使用poll或select来等待一个连接请求到达。在这种情况下,一个带有待定连接请求的套接字会以可读方式出现。


下面的代码展示一个我们可以用来分配和初始化一个被服务器进程使用的函数:



  1. #include <errno.h>
  2. #include <sys/socket.h>

  3. int
  4. initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
  5. {
  6.     int fd;
  7.     int err = 0;

  8.     if ((fd = socket(addr->sa_family, type, 0)) < 0)
  9.         return(-1);
  10.     if (bind(fd, addr, alen) < 0) {
  11.         err = errno;
  12.         goto errout;
  13.     }
  14.     if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
  15.         if (listen(fd, qlen) < 0) {
  16.             err = errno;
  17.             goto errout;
  18.         }
  19.     }
  20.     return(fd);

  21. errout:
  22.     close(fd);
  23.     errno = err;
  24.     return(-1);
  25. }

我们将看到TPC有一些关于地址重用的奇怪的规则,让这个例子不充分的。后面我们会展示这个函数的一个绕过这些规则的版本,解决这个版本的主要缺陷。
阅读(825) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~