Linux网络编程之简单并发服务器

2024-06-24 07:32

本文主要是介绍Linux网络编程之简单并发服务器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.概念

与前面介绍的循环服务器不同,并发服务器对服务请求并发处理。而循环服务器只能够一个一个的处理客户端的请求,显然效率很低. 并发服务器通过建立多个子进程来实现对请求的并发处理,但是由于不清楚请求客户端的数目,因此很难确定子进程的数目。因此可以动态增加子进程与事先分配的子进程相结合的方法来实现并发服务器。

2. 算法流程

(1)TCP简单并发服务器:

    服务器子进程1:

         socket(...);
        bind(...);
        listen(...);
        while(1)
        {
                accept(...);
                recv(....);

                process(...);

                send(.....);

       }
       close(....);//关闭客户端

    服务器子进程2:

         socket(...);
        bind(...);
        listen(...);
        while(1)
        {
                accept(...);
                recv(....);

                process(...);

                send(.....);

       }
       close(....);//关闭客户端

     服务器子进程3

              ...............

              ...............

              ...............

 (2)UDP简单并发服务器

    服务器子进程1:

       socket(...);
        bind(...);
        while(1)
        {
               
                recvfrom(....);

               process(...);

               sendto(.....);

       }

     close(...); //关闭客户端连接

   服务器子进程2,3....与1相同.

这样,同时到达多个请求,就分别由多个进程并发的处理,此算法的的性能取决于请求的数目,如果请求数目越多,这样的子进程就越多,那么进程的切换开销就会很大.而且还不容易确定子进程的数目,所以这是一种简单的并发服务器模型.

3.相关实例

(1)简单并发服务器TCP

服务器:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
TCP并发服务器,预先建立进程,同时到来的客户端分别由不同的进程并发处理
**/
#define PORT 8888
#define BUFFERSIZE 1024
#define PIDNUM 2
static void handle(int s){
  int sc;
  struct sockaddr_in client_addr;//客户端地址
  char buffer[BUFFERSIZE];
  int len;
  int ret;
  int size;
  len=sizeof(client_addr);
  time_t now; 
//接收客户端的连接
  while(1){
   memset(buffer,0,BUFFERSIZE);
   sc=accept(s,(struct sockaddr*)&client_addr,&len);
   size=recv(sc,buffer,BUFFERSIZE,0);
   if(size>0&&!strncmp(buffer,"TIME",4)){
         memset(buffer,0,BUFFERSIZE);
         now=time(NULL);
         sprintf(buffer,"%24s\r\n",ctime(&now));
         send(sc,buffer,strlen(buffer),0);//发送到客户端
  }
   close(sc);
 }

}

int main(int argc,char*argv[]){
   int s;
   int ret;
   struct sockaddr_in server_addr;
  pid_t pid[PIDNUM];
   int i;
   s=socket(AF_INET,SOCK_STREAM,0);
  if(s<0){
   perror("socket error");
   return -1;
  }
//绑定地址到套接字
  server_addr.sin_family=AF_INET;
  server_addr.sin_port=htons(PORT);
  server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
  if(ret<0){
    perror("bind error");
   return -1;
  }
 ret=listen(s,10);//监听
//建立子进程处理同时到来的客户端请求
 for(i=0;i<PIDNUM;i++){
   pid[i]=fork();
  if(pid[i]==0){
   handle(s);
  }
 }

while(1);
close(s);

}

客户端:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
 int s;
 int ret;
 int size;
 struct sockaddr_in server_addr;
 char buffer[BUFFERSIZE];
 s=socket(AF_INET,SOCK_STREAM,0);
 if(s<0){
  perror("socket error");
  return -1;
}
bzero(&server_addr,sizeof(server_addr));
//将地址结构绑定到套接字
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//连接服务器
 ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
 if(ret==-1){
  perror("connect error");
  return -1;
}
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
size=send(s,buffer,strlen(buffer),0);
if(size<0){
  perror("send error");
  return -1;
}
memset(buffer,0,BUFFERSIZE);
size=recv(s,buffer,BUFFERSIZE,0);
if(size<0){
  perror("recv error");
  return;
}

printf("%s",buffer);
close(s);
return 0;
}

运行结果:

[root@localhost 14章服务器模式]# ./circle-tcpc14 
Sat Feb 18 10:28:59 2012

(2)UDP简单并发服务器

服务器:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
UDP循环服务器处理完一个请求之后才能处理下一个请求,而不能同时处理请求
UDP并发服务器通过事先建立进程,并发的处理来自客户端的请求
缺点:事先建立好子进程,但不知道客户端的数目,难以适应动态变化
**/
//建立2个进程,并发的处理客户端的请求
#define PORT 8888
#define PIDNUM 2
#define BUFFERSIZE 1024
//两个子进程,分别并发的处理客户端的请求
static void handle(int s){
    char buffer[BUFFERSIZE];
    struct sockaddr_in client_addr;
    int len;
    int size;
    len=sizeof(client_addr);
    time_t now;
  while(1){
    memset(buffer,0,BUFFERSIZE);
    size=recvfrom(s,buffer,BUFFERSIZE,0,(struct sockaddr*)&client_addr,&len);
    if(size>0&&!strncmp(buffer,"TIME",4)){
      memset(buffer,0,BUFFERSIZE);
      now=time(NULL);
      sprintf(buffer,"%24s\r\n",ctime(&now));
     size=sendto(s,buffer,strlen(buffer),0,(struct sockaddr*)&client_addr,sizeof(client_addr));
     if(size<=0){
     perror("sendto error");
    }
    }   
}

}

int main(int argc,char*argv[]){
 int s;//套接字描述符
 int ret;//返回值
 int size;
 
 int i;
 pid_t pid[PIDNUM];
 struct sockaddr_in server_addr;
//建立套接字
 s=socket(AF_INET,SOCK_DGRAM,0);
 if(s<0){
   perror("socket error");
   return -1;
 }
 bzero(&server_addr,sizeof(server_addr));
 server_addr.sin_family=AF_INET;
 server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
 server_addr.sin_port=htons(PORT);
//将地址结构绑定到套接字描述符
 ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
 if(ret==-1){
   perror("bind error");
   return -1;
 }
//接收数据并处理,主函数建立两个进程,让进程并发的处理请求
 for(i=0;i<PIDNUM;i++){
   pid[i]=fork();
  if(pid[i]==0){
    handle(s);//让子进程去处理
}
}

while(1);//父进程等待
return 0;
}

客户端:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
UDP端,客户端向服务器发送时间请求,服务器返回相应的时间
**/
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
  int s;//套接字描述符
  int ret;//建立套接字的返回值
  int size;
  struct sockaddr_in server_addr;//地址结构
  int len;
  char buffer[BUFFERSIZE];
  s=socket(AF_INET,SOCK_DGRAM,0);//建立流式套接字 
  if(s<0){
    perror("socket error");
    return -1;
  }
 bzero(&server_addr,sizeof(server_addr));
  server_addr.sin_family=AF_INET;
  server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  server_addr.sin_port=htons(PORT);
  memset(buffer,0,BUFFERSIZE);
  strcpy(buffer,"TIME");
 //向服务器发送数据
 size=sendto(s,buffer,strlen(buffer),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
 if(size<0){
   perror("sendto error");
   return -1;
 }
 //从服务器接收数据
 len=sizeof(server_addr);
  size=recvfrom(s,buffer,BUFFERSIZE,0,(struct sockaddr*)&server_addr,&len);
 if(size<0){
   perror("recvfrom error");
  return -1;
 }
  //write(1,buffer,size);
  printf("%s\n",buffer);
 close(s);
 return 0;
}


总结:本文主要介绍了简单的TCP与UDP并发服务器,并给出了实例.这种简单的并发服务器很难适应动态变化的客户端请求,所以,接下来将介绍高级并发服务器.

这篇关于Linux网络编程之简单并发服务器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089519

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

2024.6.24 IDEA中文乱码问题(服务器 控制台 TOMcat)实测已解决

1.问题产生原因: 1.文件编码不一致:如果文件的编码方式与IDEA设置的编码方式不一致,就会产生乱码。确保文件和IDEA使用相同的编码,通常是UTF-8。2.IDEA设置问题:检查IDEA的全局编码设置和项目编码设置是否正确。3.终端或控制台编码问题:如果你在终端或控制台看到乱码,可能是终端的编码设置问题。确保终端使用的是支持你的文件的编码方式。 2.解决方案: 1.File -> S

【Altium】查找PCB上未连接的网络

【更多软件使用问题请点击亿道电子官方网站】 1、文档目标: PCB设计后期检查中找出没有连接的网络 应用场景:PCB设计后期,需要检查是否所有网络都已连接布线。虽然未连接的网络会有飞线显示,但是由于布线后期整板布线密度较高,虚连,断连的网络用肉眼难以轻易发现。用DRC检查也可以找出未连接的网络,如果PCB中DRC问题较多,查找起来就不是很方便。使用PCB Filter面板来达成目的相比DRC

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

16.Spring前世今生与Spring编程思想

1.1.课程目标 1、通过对本章内容的学习,可以掌握Spring的基本架构及各子模块之间的依赖关系。 2、 了解Spring的发展历史,启发思维。 3、 对 Spring形成一个整体的认识,为之后的深入学习做铺垫。 4、 通过对本章内容的学习,可以了解Spring版本升级的规律,从而应用到自己的系统升级版本命名。 5、Spring编程思想总结。 1.2.内容定位 Spring使用经验

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动