并发服务器之fork


server程序功能

  1. 支持多个client与server连接并recv和write
  2. 每来一个新的client,server都fork一个子进程为client服务(子进程用于与client通信:recv/write)

server程序的实现细节

  1. 父进程的while(1)循环中,一直调用accept接收新的连接
  2. 当accept接收成功后,返回新的套接字newconn,然后fork子进程与newconn通信
/*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、付费专栏及课程。

余额充值