网络编程中的地址复用和服务器支持多并发访问

  上一篇文章中所写的示例代码号称弱爆了,所言不虚!
  首先当服务器挂掉以后再启动是不能顺利的进行端口绑定的,所以我们需要解决地址复用的问题,也就是服务器端挂掉以后重启还可以进行地址绑定!
  其次,上一篇文章的程序虽然指定了客户端最大数量是10,但是服务器端的程序却只能接受一个客户端,因为其他客户端在connect之后被协议栈放到了准备建立连接的队列里面!并没有等到服务器执行accept操作,所以没有完全建立三次握手,没有新的专门用于数据传输的连接产生!所以需要让服务器端支持并发访问!
  
  

Socket API 中的地址复用

  • SO_REUSEADDR
  • 服务器端尽可能使用SO_REUSEADDR
  • 在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。
  • 使用SO_REUSEADDR选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器
  • 头文件:
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
  • 原型
 int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
  • DESCRIPTION
getsockopt()  and setsockopt() manipulate options for the socket referred to by the file descriptor sockfd.  Options
       may exist at multiple protocol levels; they are always present at the uppermost socket level.

       When manipulating socket options, the level at which the option resides and the name of the option  must  be  speci‐
       fied.   To  manipulate options at the sockets API level, level is specified as SOL_SOCKET.  To manipulate options at
       any other level the protocol number of the appropriate protocol controlling the option is supplied.  For example, to
       indicate that an option is to be interpreted by the TCP protocol, level should be set to the protocol number of TCP;
       see getprotoent(3).

       The arguments optval and optlen are used to access option values for setsockopt().  For getsockopt() they identify a
       buffer  in  which  the  value  for the requested option(s) are to be returned.  For getsockopt(), optlen is a value-
       result argument, initially containing the size of the buffer pointed to by optval, and modified on return  to  indi‐
       cate the actual size of the value returned.  If no option value is to be supplied or returned, optval may be NULL.

       Optname  and  any  specified options are passed uninterpreted to the appropriate protocol module for interpretation.
       The include file <sys/socket.h> contains definitions for socket level options, described below.   Options  at  other
       protocol levels vary in format and name; consult the appropriate entries in section 4 of the manual.

       Most  socket-level  options utilize an int argument for optval.  For setsockopt(), the argument should be nonzero to
       enable a boolean option, or zero if the option is to be disabled.

       For a description of the available socket options see socket(7) and the appropriate protocol man pages.
  • RETURN VALUE
    On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

简单多并发

这里写图片描述

这里写图片描述

这里写图片描述

修改后的服务器代码–还是弱爆了

/*server02*/

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MAX_CLIENT 10
#define MAX_READ  1024

int main()
{

  int serv_fd,con_fd;//服务器端至少要有两个套接字文件描述符--一个用来监听,一个/其余多个用来和客户端通信
  struct sockaddr_in serv_addr;//IPV4套接字结构体--服务器
  struct sockaddr_in clt_addr;//IPV4套接字结构体--客户端
  char addr_dst[INET_ADDRSTRLEN] = {0};

  char read_buf[MAX_READ]={0};
  int optvar;
  pid_t pid;

  socklen_t addr_len;
  int i = 0;
  ssize_t ret ;

  serv_fd = socket(AF_INET,SOCK_STREAM,0);
  if(-1 == serv_fd)
  {
    perror("socket");
    exit(-1);
  }

  if(setsockopt(serv_fd, SOL_SOCKET,SO_REUSEADDR,&optvar,sizeof(optvar))  == -1 )
  {
    perror("setsockopt");
    exit(-1);
  }

  bzero(&serv_addr,sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(8001);
  serv_addr.sin_addr.s_addr = htons(INADDR_ANY);

  if(bind(serv_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) == -1)
  {
    perror("bind");
    exit(-1);
  }

  /*一旦调用listen函数--套接字就会变成被动套接字--用来监听客户端,让客户端连接他
  被动套接字--只能接受连接,不能主动发送连接
  做了两个队列:
          一个已经完成三次握手,建立连接的队列--客户端发connect请求被响应,已经成功完成连接
          一个是未完成成三次握手的队列--正在握手
  */
  if(listen(serv_fd,MAX_CLIENT)== -1)
  {
    perror("listen");
    exit(-1);
  }

  addr_len = sizeof(clt_addr);

  printf("Accepting connections ...\n");


  while(1)
  {
      if((con_fd = accept(serv_fd,(struct sockaddr *)&clt_addr,&addr_len)) == -1)
      {
        perror("accept");
        exit(-1);
      }

      pid = fork();//创建子进程,每一个子进程处理一个客户端的连接

      if(pid == 0)//子进程业务逻辑
      {
        printf("received from %s at PORT %d\n",inet_ntop(AF_INET,&clt_addr.sin_addr,addr_dst,sizeof(addr_dst)),ntohs(clt_addr.sin_port));
        close(serv_fd);//子进程关闭监听套接字--子进程只负责在建立连接的套接字上进行数据传输
        while(1)
        {
          ret = read(con_fd,read_buf,sizeof(read_buf));
          if(ret == 0)
          {
            /*如果在读的过程中,对方已关闭,TCP/IP协议会返回一个0数据包*/
            printf("client close\n");
            close(con_fd);
            exit(-1);
          }
          else if(ret < 0)
          {
            perror("read");
            exit(-1);
          }

          fputs(read_buf,stdout);//打印内容

          for(i = 0; i < ret ;i++)
          {
            read_buf[i] = toupper(read_buf[i]);
          }

          write(con_fd,read_buf,ret);//回发报文
          memset(read_buf,0,sizeof(read_buf));

        }
      }
      else if(pid == -1)//创建子进程出错
      {
        perror("fork");
        close(serv_fd);
        close(con_fd);
        exit(-1);
      }
      else if(pid > 0)
      {
        close(con_fd);//父进程关闭传输套接字--一旦建立连接。数据传输的任务交给子进程,父进程只负责监听有没有新的客户端发起请求
      }


  }



  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值