2020/2//17 服务端并发处理

1. 在服务器端的do_service下增加这样一段代码

size_t len;
    char buff[20];    
    if((len = read(fd, buff, 20)) < 0){
        perror("read error");//先读取来源于客户端信息,IO都是阻塞读写,如果客户端没有写操作,这里会被阻塞。
    }

重新编译运行会发现,客户端收不到服务器write返回的系统时间,服务端阻塞在read上。

一个客户端出问题,整个服务器都会停下来,这样的代码是低效的。

服务器端并发性处理分为三种:多进程模型、多线程模型、多路转换IO模型。

2.多进程模型

管道的读写特性 1.通过打开两个管道来创建一个双向的管道; 2.管道默认是阻塞性的,当进程从管道中读取数据,若没有数据进程会阻塞; 3.当一个进程往管道中不断地写入数据但是没有进程去读取数据,此时只要管道没有满是可以写的,但若管道放满数据的则会报错;

不完整管道:1.read端关闭,write写数据时,会产生信号SIGPIPE,write返回-1,erno=EPIPE;2.write端关闭,read读数据时,read返回0,表示已经读到文件末尾。

【服务端采用多进程模型】

echo_tcp_client.c:

void do_service(int fd)
{
    //和客户端进行读写操作(双向通信)
    char buff[512];
    while(1){
        memset(buff, 0, sizeof(buff));
        printf("start read and write...\n");
        size_t size;
        if((size = read_msg(fd, buff, sizeof(buff))) < 0){//这里采用了自定义读取信息的协议,需要包括“msg.h”头文件
            perror("protocal error");
            break;
        }
        else if(size == 0){//如果客户端read已经关闭,跳出进程
            break;
        }
        else{
            printf("%s\n", buff);
            if(write_msg(fd, buff, sizeof(buff)) < 0){//如果客户端write已经关闭,产生EPIPE,跳出进程
                if(errno == EPIPE){
                    break;
                }
                perror("protocal error");
            }
        }
    }
}

main(){

.......

while(1){
        int fd = accept(sockfd,    (struct sockaddr*)&clientaddr, &clientaddr_len);
        if(fd < 0) {
             perror("accept error");
            continue;
        }

        /*step 5 IO function: read/write 启动子进程去调用IO函数*/
         
        pid_t pid = fork();
        if(pid < 0){
            continue;
        }
        else if(pid == 0){  //child process
            out_addr(&clientaddr);
            do_service(fd);        
            close(fd);
            break;
        }    
        else{                //parent process
            close(fd);

         }

.....

}

通过fork进行系统调用,产生一个与原进程完全相同的进程,复制了包括fd在内的进程作为子进程。所以我们在父进程中,对原先的fd进行关闭操作即可。

pid_t类型的pid表示fork子进程的返回值。在父进程中,返回值是新创建的子进程的进程ID;子进程中,返回0;错误返回一个负值。进程创建错误的原因有以下两种:1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。  2)系统内存不足,这时errno的值被设置为ENOMEM。

【客户端从标准输入流中获取要发送的信息】

    /*step 3 IO function: read/write 双向通信*/
    char buff[512];
    size_t size;
    char *prompt = ">";//提示符
    while(1){
        memset(buff, 0, sizeof(buff));
        write(STDOUT_FILENO, prompt, 1);//先在客户端终端输出一个 > 提示符
        size = read(STDIN_FILENO, buff, sizeof(buff));
        if(size < 0) continue;
        buff[size - 1] = '\0';

        if(write_msg(sockfd, buff, sizeof(buff)) < 0){
            perror("write msg error");
            continue;
        }
        else{
            if(read_msg(sockfd, buff, sizeof(buff)) < 0){
                perror("read msg error");
                continue;
            }
            else{
                printf("%s\n", buff);
            }            
        }
    }

客户端向服务端发送一个message,然后服务端返回一个一样的信息输出在客户端界面

结果如下:

3. shell中的echo命令

echo:用于字符串输出,在显示器上显示echo后面跟的字符串or执行结果

$# 表示参数个数

$0 是脚本本身的名字

$1 是传递给该shell脚本的第一个参数

$2 是传递给该shell脚本的第二个参数

$@ 表示所有参数,并且所有参数都是独立的

$$ 是脚本运行的当前进程ID号

$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误

$SHELL 返回的是/bin/bash

4. 程序报错缺少头文件解决办法:man wait(需要头文件的函数名)就能知道需要包含什么头文件了

发布了7 篇原创文章 · 获赞 4 · 访问量 551

猜你喜欢

转载自blog.csdn.net/Xinyue_Lu/article/details/104357019