133-接受连接

来源:互联网 发布:永久域名发布器网站 编辑:程序博客网 时间:2024/04/29 15:07

1. 被动 socket 与 主动 socket

前面几讲以来,一直未提到这个概念。实际上,使用 socket 函数创建的套接字(插座)默认情况下就是主动 socket。

主动 socket,是指未来你将要用此 socket 主动发起连接,即你将要把它传递给 connect 函数。

被动 socket,也被称为监听 socket,它不能用作 connect 函数的参数。这个套接字,只能用来接受连接请求。


这里写图片描述
图1 主动 socket 与被动 socket

这样一来,我们就能明白,即使我们使用 socket 创建了一个套接字并 bind 到了套接字地址上,这个进程也无法接受请求,除非我们将此 socket 转换成被套接字。

如何将主动 socket 设置成被动 socket 的呢?对应到图 1 中,就是那个紫色的插座,它原先是个绿色的,要怎么把它变成紫色的?

函数 listen 可以完成这个操作。

2. listen 函数

(1) 函数原型

int listen(int sockfd, int backlog);

(2) 函数语义

1) 如果套接字 sockfd 还未绑定套接字地址,listen 会选择本地地址,并随机选择一个端口号绑定到 sockfd 上。(这一条规则蕴含的意思是,作为服务器,可以不调用 bind 函数,但是通常不这么干。

2) listen 函数将主动套接字 sockfd 转换成被套接字。参数 backlog 用来指定未决连接队列大小。

未决连接队列,是指接收到连接请求,但是尚未被处理(未被 accept,后面会说)的连接的个数。当一个进程使用 connect 函数发起请求后,服务器进程的被动套接字就会收到连接请求,然后检查未决连接队列是否有空位,如果未决队列满了,就会拒绝连接,connect 函数返回失败;如果未决连接队列有空位,就将该连接加入未决连接队列。当 connect 函数成功返回后,著名的“三次握手”就未完成了。

那么一般 backlog 设置成多大合适呢?这个看具体需求,当然,我们也可以指定一个最大值,通常由宏 SOMAXCONN 指定。

注1:在不同版本的内核实现中,未决连接队列大小实现方法是不一样的,有些可能会取 backlog 和 SOMAXCONN 之间的较大值。所以如果你指定了一个较小的 backlog,却发现可以连接超过 5 个客户端也不要惊讶。

注2:未决连接队列里头又被划分成两个队列,分别是未完成队列已完成队列,有关这些知识,将在 Linux 网络编和学习笔记中详解。

3. 实验

程序 serv.c 将主动 socket 转换成被动 socket。

  • 代码
#include <stdio.h>#include <stdlib.h>#include <arpa/inet.h>#define ERR_EXIT(msg) do { perror(msg); exit(1); } while(0)int main() {  struct sockaddr_in servaddr, cliaddr;  int sockfd, clientfd, ret;  socklen_t cliaddrlen;  // 1. create sockaddr  puts("1. create sockaddr");  servaddr.sin_family = AF_INET;  // 注意,把这个 ip 地址改成你配置的地址。  servaddr.sin_addr.s_addr = inet_addr("192.168.80.130");  servaddr.sin_port = htons(8080);  // 2. create socket  puts("2. create socket");  sockfd = socket(AF_INET, SOCK_STREAM, 0);   if (sockfd < 0) ERR_EXIT("socket");  // 3. bind sockaddr  puts("3. bind sockaddr");  ret = bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));  if (ret < 0) ERR_EXIT("bind");  // 4. listen  puts("4. listen");  ret = listen(sockfd, 5);   if (ret < 0) ERR_EXIT("listen");  // 在这里等待连接请求  while(1) pause();  return 0;}
  • 编译和运行
$ gcc serv.c -o serv$ ./serv


这里写图片描述
图2 主动套接字转换成被套接字后,就可以接受连接请求了

可以通过命令 netstat -an | grep tcp 查看网络的状态:


这里写图片描述
图3 网络状态

可以看到,我们的进程已经处于一种 LISTEN 的状态啦!

接下来,我们把上一节写好的 cli.c 程序拿过来,运行一下看看,不过有几个地方需要改改,就是连接的 ip 地址。新的 cli.c 程序见第 4 节。


这里写图片描述
图4 cli 程序运行结果

4. cli.c 程序

#include <stdio.h>#include <stdlib.h>#include <arpa/inet.h>#define ERR_EXIT(msg) do { perror(msg); exit(1); } while(0)int main() {  int sockfd, ret;  struct sockaddr_in servaddr;  struct sockaddr_in cliaddr;  socklen_t cliaddrlen;  servaddr.sin_family = AF_INET;  // 连接地址改成服务器监听的那个地址  servaddr.sin_addr.s_addr = inet_addr("192.168.80.130");  servaddr.sin_port = htons(8080);  sockfd = socket(AF_INET, SOCK_STREAM, 0);   if (sockfd < 0) ERR_EXIT("socket");  ret = connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));  if (ret < 0) ERR_EXIT("connect");  puts("connect successful");  cliaddrlen = sizeof(cliaddr);  ret = getsockname(sockfd, (struct sockaddr*)&cliaddr, &cliaddrlen);  if (ret < 0) ERR_EXIT("getsockaddr");  printf("cliaddr: %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));  //while(1) pause();  return 0;}

5. 总结

  • 主动 socket 和被动 socket 的区别
  • 掌握 listen 函数
  • 知道什么是未决连接队列
0 0
原创粉丝点击