首页 > 系统相关 >《TCP/IP网络编程》(第十章)多进程服务器端(2)

《TCP/IP网络编程》(第十章)多进程服务器端(2)

时间:2024-05-27 19:04:52浏览次数:29  
标签:serv 服务器端 int IP sock TCP error 客户端 addr

基于进程的并发服务器

我们将扩展之前的回声服务器,使其可以同时向多个客户端体提供服务,实现模型如下图所示
在这里插入图片描述
即每当有客户端向服务器请求服务时,服务器端都创建一个子进程为其提供服务,比如有5个客户端请求服务,则创建个5子进程。

通过fork()复制的文件描述符
下图是父进程调用fork()函数后的示意图
在这里插入图片描述
分割TCP的I/O程序
即客户端的父进程负责接收数据,额外创建的子进程负责发送数据,分割后互不干扰,这样无论客户端是否完成从服务器接收完数据,都可以进行发送,可以提高频繁交换数据的程序性能
在这里插入图片描述

服务器代码
扩展之前的回声服务器,使其可以同时向多个客户端体提供服务。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> // POSIX标准定义的通用函数,如close()
#include <arpa/inet.h> // 提供inet相关的函数,如inet_addr()
#include <sys/socket.h> // 提供socket相关的函数和数据结构
#include <signal.h>
#include <sys/wait.h>

#define BUFF_SIZE 30 //缓冲区大小
void error_handling(char* message);
void rea_childproc(int sig);

int main(int argc, char *argv[]) 
{
    int serv_sock; // 服务器套接字
    int clnt_sock; // 客户端套接字
    struct sockaddr_in serv_addr; // 服务器地址结构
    struct sockaddr_in clnt_addr; // 客户端地址结构

    pid_t pid;
    socklen_t clnt_addr_size; // 客户端地址结构的大小
    struct sigaction act;
    char opinfor[BUFF_SIZE];
	int str_len,state;

    
    if(argc!=2)
    {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1); 
    }

    act.sa_handler=rea_childproc;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    sigaction(SIGCHLD, &act, 0);

    // 创建一个服务器套接字
    serv_sock=socket(PF_INET, SOCK_STREAM, 0);//使用SOCK_STREAM创建TCP套接字
    if(serv_sock==-1) 
        error_handling("socket() error"); 

    // 初始化服务器地址结构
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family=AF_INET; // 地址族设置为IPv4
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); // 服务器地址设置为任意
    serv_addr.sin_port=htons(atoi(argv[1])); // 设置监听端口为命令行参数指定的端口

    // 绑定套接字,调用bind()函数分配ip地址和端口号
    if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
        error_handling("bind() error"); 
    
    if(listen(serv_sock, 5)==-1) // 监听套接字,最多监听5个连接
        error_handling("listen() error");
    
    while(1){
        clnt_addr_size=sizeof(clnt_addr);
        clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
        if(clnt_sock==-1)
            error_handling("accept() error");
        else
            puts("New Client Connected...");
        pid=fork();
        if(pid==0){
            close(serv_sock);
            while((str_len=read(clnt_sock, opinfor, BUFF_SIZE))!=0)
                write(clnt_sock, opinfor, str_len);
            close(clnt_sock);
            printf("client disconnected...");
            return 0;
        }
        else
            close(clnt_sock);
            continue;
    }


    // 关闭客户端和服务器套接字
    close(serv_sock);
    
    return 0; 
}

void error_handling(char *message)
{
    fputs(message, stderr); 
    fputc('\n', stderr); 
    exit(1); 
}

void rea_childproc(int sig)
{
    pid_t pid;
    int status;
    pid=waitpid(-1, &status, WNOHANG);
    if(WIFEXITED(status))
        printf("Removed proc id: %d \n", pid);
}

客户端代码
分割了TCP的I/O程序的客户端

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <sys/socket.h> 

#define BUF_SIZE 30
void error_handling(char *message); 
void read_routline(int sock, char *buf);
void write_routline(int sock, char *buf);

int main(int argc, char *argv[]) 
{
    int sock; // 客户端套接字
    pid_t pid;
    struct sockaddr_in serv_addr; // 服务器地址结构
    char message[BUF_SIZE]; // 用于存储从服务器接收的消息
    int str_len,i; // 读取的字节数

    
    if (argc != 3)
    {
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1); 
    }

    // 创建一个客户端套接字
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1) 
        error_handling("socket() error"); 

    // 初始化服务器地址结构
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET; // 地址族设置为IPv4
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址
    serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号

    // 发送连接请求
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
        error_handling("connect() error"); 
        else{
            printf("Connected ok\n");
        }
    
    pid=fork();
    if(pid==0){
        write_routline(sock,message);
    }else{
        read_routline(sock,message);
        close(sock);
        return 0;
    }

}


void error_handling(char* message){
    fputs(message, stderr); 
    fputc('\n', stderr); 
    exit(1); 
}

void write_routline(int sock, char *buf){
    while(1){
        fgets(buf, BUF_SIZE, stdin);
        if(!strcmp(buf,"q\n")||!strcmp(buf,"Q\n")){
            shutdown(sock, SHUT_WR);
            return;
        }
        write(sock, buf, strlen(buf));
        
    }
}

void read_routline(int sock, char *buf){
    while(1){
        int str_len=read(sock, buf, BUF_SIZE);
        if(str_len==0){
            return;
        }
        buf[str_len]=0;
        printf("Message from server:%s", buf);
    }
}

运行结果
在这里插入图片描述

可以同时为3个客户端提供回声服务。

标签:serv,服务器端,int,IP,sock,TCP,error,客户端,addr
From: https://blog.csdn.net/m0_53115174/article/details/139242857

相关文章

  • 互斥锁、进程间通信(IPC)、队列(queue)模块、队列实现进程间通信、生产者和消费者模型
    【一】互斥锁【1】什么是进程同步(互斥锁)互斥锁(Mutex)是一种用于多线程编程中控制对共享资源访问的机制。其作用是保证在同一时刻只有一个线程在访问共享资源,从而避免多个线程同时读写数据造成的问题。互斥锁的基本原理是在对共享资源进行访问前加锁,使得其他线程无法访问该......
  • [转载]TCP keepalive的详解(解惑)
    原文出自于https://www.cnblogs.com/lanyangsh/p/10926806.htmlTCP是面向连接的,一般情况,两端的应用程序可以通过发送和接收数据得知对端的存活。当两端的应用程序都没有数据发送和接收时,如何判断连接是否正常呢?这就是SO_KEEPALIVE的作用。1.SO_KEEPALIVE的作用1.1SO_KEEPA......
  • TCP_UNACCEPTABLE_14: [close-wait] out-of-wdw SEQ | unacceptable ACK -> ACK (seq,
    测试目的:验证TCP在CLOSE-WAIT状态下,接收到一个窗口外的序列号或不可接受的ACK号的段时,是否能够返回一个带有正确的序列号和ACK号的ACK段,并保持在相同的状态。描述:在TCP连接的CLOSE-WAIT状态下,如果接收到一个序列号超出当前窗口或ACK号不可接受的段,TCP必须回应一个空的ACK......
  • CSP历年复赛题-P1057 [NOIP2008 普及组] 传球游戏
    原题链接:https://www.luogu.com.cn/problem/P1057题意解读:n个人围一圈,从1开始传球m次,每次可以往左或右传,计算球再次传给1的方案数。解题思路:求方案数,通常就是DP问题,此题DP状态并不难想,如果实在不会,也可以通过DFS暴搜得部分分。1、DFS60分代码:#include<bits/stdc++.h>using......
  • CI工具Jenkins本地部署结合内网穿透实现无公网IP访问Jenkins站点
    文章目录1.安装Jenkins2.局域网访问Jenkins3.安装cpolar内网穿透软件4.配置Jenkins公网访问地址5.公网远程访问Jenkins6.固定公网地址本文主要介绍如何在LinuxCentOS7中安装Jenkins并结合cpolar内网穿透工具实现远程访问管理本地部署的Jenkins服务.Jenkins......
  • TypeScript中的`let`、`const`、`var`区别:变量声明的规范与实践
    TypeScript中的let、const、var区别:变量声明的规范与实践引言在TypeScript中,变量声明是代码编写的基础部分。let、const、var是三种用于变量声明的关键字,它们各自有不同的作用域规则和可变性特点。基础知识作用域:变量可以在整个文件(全局作用域)或某个特定代码块(局部作用......
  • clip-cnblog
    CLIPgithubLearningTransferableVisualModelsFromNaturalLanguageSupervisionCLIP全称ConstrastiveLanguage-ImagePre-training,是OpenAI推出的采用对比学习的文本-图像预训练模型。CLIP惊艳之处在于架构非常简洁且效果好到难以置信,在zero-shot文本-图像检索,zero-sho......
  • 在JavaScript中如何移除数组中的特定项
    在日常开发中,我们经常需要从数组中移除某个特定的元素。在JavaScript中,存在多种不同的方法来完成这一任务,本文将总结几种常见的处理方式,并介绍它们的优缺点。常规情况1.使用.splice()方法按值移除数组元素是否修改原数组:是是否移除重复项:是(使用循环),否(使用i......
  • 代码雨(coderain)源码(html5+css3+javascript,原创)
     大家看过黑客帝国的代码雨吗?本人自己写了一个,效果还可以。演示效果请见https://www.lanbaoshi.site/coderain.htm下面上代码:<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><htmlxmlns="htt......
  • 小程序自定义swiper的指示点样式及颜色
    1图居中、圆边角、指示点颜色更改、指示点样式更改下图是样式不好看的组件要修改成这样::  wxml:<swiperclass="bd"indicator-dots="{{true}}"indicator-active-color="#ff8f00"autoplay="{{true}}"interval="5000"duration="500">......