Linux多进程并发服务器(TCP)

版权声明:本文为博主原创文章,转载请备注https://blog.csdn.net/travelerwz。 https://blog.csdn.net/Travelerwz/article/details/82106579

Linux多进程并发服务器(TCP)

前言:在Linux环境下多进程的应用很多,其中最主要的就是网络/客户服务器。多进程服务器是当客户有请求时 ,服务器用一个子进程来处理客户请求。父进程继续等待其它客户的请求。这种方法的优点是当客户有请求时 ,服务器能及时处理客户 ,特别是在客户服务器交互系统中。对于一个 TCP服务器,客户与服务器的连接可能并不马上关闭 ,可能会等到客户提交某些数据后再关闭 ,这段时间服务器端的进程会阻塞 ,所以这时操作系统可能调度其它客户服务进程。比起循环服务器大大提高了服务性能。

我们来看一下并发服务器和循环服务器的区别:

snipaste_20180826_221138
这里写图片描述

TCP**并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。**

int main()
{
创建套接字socket
绑定(bind)套接字
监听(listen)套接字sockfd
while(1)

{

   int connfd = accpet(...);

   if(fork(...) == 0)//子进程

   {
    close(sockfd)//关闭监听套接字,从父进程继承过来的
    process(...);

    close(connfd);//关闭已连接的套接字
    exit(0);
   }
   close(connfd);
}
close(sockfd)
}

fork函数在并发服务器中的应用:
  父、子进程各自执行不同的程序段,这是非常典型的网络服务器。父进程等待客户 的服务请求。当这种请求到达时,父进程调用 fork 函数,产生一个子进程,由子进程对该请求作处理。父进程则继续等待下一个客户的服务请求。并且这种情况下,在 fork 函数之后,父、子进程需要关闭各自不使用的描述符,即父进程将不需要的 已连接描述符关闭,而子进程关闭不需要的监听描述符。这么做的原因有3个:

  1. 节省系统资源
  2. 防止上面提到的父、子进程同时对共享描述符进程操作
  3. 最重要的一点,是确保close函数能够正确关闭套接字描述符

我们在socket编程中调用 close 关闭已连接描述符时,其实只是将访问计数值减 1。而描述符只在访 问计数为 0 时才真正关闭。所以为了正确的关闭连接,当调用 fork 函数后父进程将不需要的 已连接描述符关闭,而子进程关闭不需要的监听描述符。

例子:

utili.h

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<signal.h>
#define LISTENQ 1024
#define SERV_PORT 9857

#define BUFSIZE 4096

ser.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<signal.h>
#define LISTENQ 1024
#define SERV_PORT 9857

#define BUFSIZE 4096
wz@wz-machine:~/linux/socket/2$ cat ser.c 
#include"utili.h"

void str_echo(int sockfd)
{
    ssize_t n;
    char buf[BUFSIZE];
    while((n=read(sockfd,buf,BUFSIZE)) > 0)
        write(sockfd,buf,n);
}
void sig_chld(int signo)
{
    pid_t pid;
    int stat;
    while((pid = waitpid(-1,&stat,WNOHANG))>0)
        printf("child %d terminated\n",pid);
    return ;
}
int main()
{
    int confd,listenfd;
    struct sockaddr_in cliaddr,servaddr;
    pid_t childpid;
    socklen_t clien;
    int status;
    char buf[BUFSIZE];

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    listenfd = socket(AF_INET,SOCK_STREAM,0);
    if(listenfd == -1)
        printf("socket error\n");
    status = bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    if(status == -1)
        printf("bind error\n");
    status = listen(listenfd,LISTENQ);
    signal(SIGCHLD,sig_chld);
    while(1)
    {
        clien = sizeof(cliaddr);
        confd = accept(listenfd,(struct sockaddr*)&cliaddr,&clien);
        if(confd == -1)
            printf("accept error\n");
        if((childpid = fork())==0)
        {
            printf("connet from %s,port %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,buf,sizeof(buf)),ntohs(cliaddr.sin_port));
            close(listenfd);
            str_echo(confd);
            close(confd);
            exit(0);
        }
        close(confd);
    }
//  return 0;
}

cli.c

#include"utili.h"

void str_cli(FILE* fp,int sockfd)
{
    printf("connect success\n");
    char send[BUFSIZE],recv[BUFSIZE];
    while(fgets(send,BUFSIZE,fp)!=NULL)
    {
        write(sockfd,send,strlen(send));
        read(sockfd,recv,BUFSIZE);
        fputs(recv,stdout);
        bzero(recv,BUFSIZE);
    }
}

int main(int argc,char **argv)
{
    int sockfd;
    struct sockaddr_in servaddr;
    socklen_t clien;
    int status;
    char buf[BUFSIZE];

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
//  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET,argv[1],&servaddr.sin_addr);

    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
        printf("socket error\n");
    status = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    if(status == -1)
        printf("connecd error\n");
    str_cli(stdin,sockfd);
    return 0;
//  exit(0);
}

这就是一个很典型的例子,看具体情况请看TCP回射服务器

猜你喜欢

转载自blog.csdn.net/Travelerwz/article/details/82106579