前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >并发服务器代码实现(多进程/多线程)

并发服务器代码实现(多进程/多线程)

作者头像
mindtechnist
发布于 2025-05-09 04:16:08
发布于 2025-05-09 04:16:08
8200
代码可运行
举报
文章被收录于专栏:机器和智能机器和智能
运行总次数:0
代码可运行

什么是并发服务器

当涉及到构建高性能的服务器应用程序时,我们通常会考虑使用并发服务器来处理多个客户端请求。在并发服务器中,多进程和多线程是两种常见的并发模型,它们都有各自的优点和适用场景。本文将介绍多进程和多线程并发服务器的基础知识。

多进程并发服务器

多进程并发服务器通过创建多个子进程来处理客户端请求。每个子进程是操作系统中独立运行的单位,拥有自己的内存空间和资源。当有新的客户端连接请求到达时,服务器创建一个新的子进程来处理该请求。子进程负责与客户端通信并提供所需的服务。

多进程并发服务器的优点是稳定性高。由于每个子进程都是相互独立的,一个子进程的崩溃或错误不会影响其他子进程的执行。这种独立性使得多进程并发服务器能够有效地隔离错误,提高服务器的可靠性。

然而,多进程并发服务器也有一些缺点。创建和管理多个进程需要消耗更多的系统资源,包括内存和CPU时间。进程间的通信也需要特殊的机制,例如管道或共享内存,以便在不同进程之间传递数据。此外,由于每个进程都有自己的内存空间,进程间的数据共享和同步可能会变得复杂。

多线程并发服务器

多线程并发服务器通过创建多个线程来处理客户端请求。线程是在进程内部运行的独立执行流,共享同一个进程的内存空间和资源。与多进程不同,多线程服务器不需要创建新的进程来处理请求,而是在同一个进程中创建多个线程。

多线程并发服务器的优点是资源消耗较少。与进程相比,线程的创建和切换开销更小,因为它们共享进程的资源。这使得多线程并发服务器更加轻量级,能够更高效地利用系统资源。

然而,多线程并发服务器也存在一些问题。首先,线程共享进程的内存空间,因此在多线程环境中访问共享数据需要特殊的同步机制,以避免竞态条件和数据不一致。其次,由于线程共享相同的地址空间,一个线程的错误可能会影响整个进程,导致服务器崩溃或不稳定。

选择适合的并发模型

在选择多进程还是多线程并发服务器时,需要根据具体的应用需求和性能要求进行权衡。以下是一些建议:

- 如果稳定性和容错性是首要考虑因素,多进程并发服务器可能是更好的选择。每个子进程的独立性可以有效地隔离错误,提高服务器的可靠性。

- 如果服务器需要处理大量的并发连接并需要更高的性能和资源利用率,多线程并发服务器可能更适合。线程的创建和切换开销相对较小,可以更高效地处理并发请求。

- 如果同时需要稳定性和性能,可以考虑使用混合模型,即在每个进程中创建多个线程,以实现更好的负载平衡和资源利用率。

无论选择多进程还是多线程并发服务器,都需要注意正确处理并发访问共享数据的问题,使用适当的同步机制(如锁、信号量)来保证数据的一致性和正确性。

总结起来,多进程和多线程并发服务器是实现高性能服务器的常见方式。它们各有优劣,选择合适的并发模型需要考虑应用需求和性能要求,并注意处理并发访问共享数据的问题。

多进程并发服务器代码实现

使用多进程并发服务器时要考虑以下几点:

- 父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符);

- 系统内创建进程个数(与内存大小相关);

-进程创建过多是否降低整体服务性能(进程调度);

server

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* server.c */
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/types.h>
#include"wrap.h"

#define MAXLINE 80
#define SERV_PORT 800

voiddo_sigchild(int num)
{
while (waitpid(0, NULL, WNOHANG) > 0)
    ;
}
intmain(void)
{
structsockaddr_inservaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, n;
pid_t pid;

structsigactionnewact;
  newact.sa_handler = do_sigchild;
  sigemptyset(&newact.sa_mask);
  newact.sa_flags = 0;
  sigaction(SIGCHLD, &newact, NULL);

  listenfd = Socket(AF_INET, SOCK_STREAM, 0);

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

  Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

  Listen(listenfd, 20);

printf("Accepting connections ...\n");
while (1) {
    cliaddr_len = sizeof(cliaddr);
    connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);

    pid = fork();
if (pid == 0) {
      Close(listenfd);
while (1) {
        n = Read(connfd, buf, MAXLINE);
if (n == 0) {
printf("the other side has been closed.\n");
break;
        }
printf("received from %s at PORT %d\n",
            inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
            ntohs(cliaddr.sin_port));
for (i = 0; i < n; i++)
          buf[i] = toupper(buf[i]);
        Write(connfd, buf, n);
      }
      Close(connfd);
return0;
    } elseif (pid > 0) {
      Close(connfd);
    } else
      perr_exit("fork");
  }
  Close(listenfd);
return0;
}

client:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* client.c */
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include"wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666

intmain(int argc, char *argv[])
{
structsockaddr_inservaddr;
char buf[MAXLINE];
int sockfd, n;

  sockfd = Socket(AF_INET, SOCK_STREAM, 0);

  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
  servaddr.sin_port = htons(SERV_PORT);

  Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

while (fgets(buf, MAXLINE, stdin) != NULL) {
    Write(sockfd, buf, strlen(buf));
    n = Read(sockfd, buf, MAXLINE);
if (n == 0) {
printf("the other side has been closed.\n");
break;
    } else
      Write(STDOUT_FILENO, buf, n);
  }
  Close(sockfd);
return0;
}

多线程并发服务器代码实现

在使用线程模型开发服务器时需考虑以下问题:

- 调整进程内最大文件描述符上限;

- 线程如有共享数据,考虑线程同步;

- 服务于客户端线程退出时,退出处理(退出值,分离态);

- 系统负载,随着链接客户端增加,导致其它线程不能及时得到CPU;

server:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* server.c */
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>

#include"wrap.h"
#define MAXLINE 80
#define SERV_PORT 6666

structs_info {
structsockaddr_incliaddr;
int connfd;
};
void *do_work(void *arg)
{
int n,i;
structs_info *ts = (structs_info*)arg;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
/* 可以在创建线程前设置线程创建属性,设为分离态,哪种效率高内?*/
  pthread_detach(pthread_self());
while (1) {
    n = Read(ts->connfd, buf, MAXLINE);
if (n == 0) {
printf("the other side has been closed.\n");
break;
    }
printf("received from %s at PORT %d\n",
        inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
        ntohs((*ts).cliaddr.sin_port));
for (i = 0; i < n; i++)
      buf[i] = toupper(buf[i]);
    Write(ts->connfd, buf, n);
  }
  Close(ts->connfd);
}

intmain(void)
{
structsockaddr_inservaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
int i = 0;
pthread_t tid;
structs_infots[256];

  listenfd = Socket(AF_INET, SOCK_STREAM, 0);

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

  Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
  Listen(listenfd, 20);

printf("Accepting connections ...\n");
while (1) {
    cliaddr_len = sizeof(cliaddr);
    connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
    ts[i].cliaddr = cliaddr;
    ts[i].connfd = connfd;
/* 达到线程最大数时,pthread_create出错处理, 增加服务器稳定性 */
    pthread_create(&tid, NULL, do_work, (void*)&ts[i]);
    i++;
  }
return0;
}

client:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* client.c */
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include"wrap.h"
#define MAXLINE 80
#define SERV_PORT 6666
intmain(int argc, char *argv[])
{
structsockaddr_inservaddr;
char buf[MAXLINE];
int sockfd, n;

  sockfd = Socket(AF_INET, SOCK_STREAM, 0);

  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
  servaddr.sin_port = htons(SERV_PORT);

  Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

while (fgets(buf, MAXLINE, stdin) != NULL) {
    Write(sockfd, buf, strlen(buf));
    n = Read(sockfd, buf, MAXLINE);
if (n == 0)
printf("the other side has been closed.\n");
else
      Write(STDOUT_FILENO, buf, n);
  }
  Close(sockfd);
return0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器和智能 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信
在构建网络应用时,常常会接触到各种网络协议,其中TCP(Transmission Control Protocol)作为一种面向连接、可靠的传输层协议,扮演着至关重要的角色。理解TCP协议及其在服务器开发中的应用,是每一位网络编程初学者的必经之路。
Lion 莱恩呀
2025/05/13
1180
【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信
TCP并发服务器(多进程与多线程)
我们在上一节写的TCP服务器只能处理单连接,在代码实现时,多进程并发服务器与非并发服务器在创建监听套接字、绑定、监听这几个步骤是一样的,但是在接收连接请求的时候,多进程并发服务器是这样实现的:父进程负责接受连接请求,一旦连接成功,将会创建一个子进程与客户端通信。示意图如下:
mindtechnist
2024/10/08
2800
TCP并发服务器(多进程与多线程)
I/O复用——单进程服务器(select版)
为了可以处理多个客户的请求,我们之前一直使用多进程TCP并发服务器,socket()监听一个套接口,accept()多个用户,父进程监听listenfd,子线程们在connfd上进行应答处理。
jackieluo
2019/01/06
2.1K0
I/O复用——单进程服务器(select版)
epoll,求知者离我近点
上网一搜epoll,基本是这样的结果出来:《多路转接I/O – epoll模型》,万变不离这个标题。 但是呢,不变的事物,我们就更应该抓出其中的重点了。 多路、转接、I/O、模型。 别急,先记住这几个词,我比较喜欢你们看我文章的时候带着问题。
看、未来
2020/08/25
5390
epoll,求知者离我近点
TCP服务器的演变过程:揭秘使用多线程实现一对多的TCP服务器
在构建网络应用时,常常会接触到各种网络协议,其中TCP(Transmission Control Protocol)作为一种面向连接、可靠的传输层协议,扮演着至关重要的角色。理解TCP协议及其在服务器开发中的应用,是每一位网络编程初学者的必经之路。
Lion 莱恩呀
2025/05/15
820
TCP服务器的演变过程:揭秘使用多线程实现一对多的TCP服务器
select()函数详解
http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html
bear_fish
2018/09/20
1.9K0
select()函数详解
TCP服务器的演变过程:多进程实现一对多的TCP服务器
本系列文章旨在带领大家逐步深入TCP服务器的开发世界,从最基础的一对一连接通信开始,逐步探索更复杂、更高效的服务器模型。从零开始,手把手地编写第一个TCP服务器程序,亲身体验“开局一块砖,大厦全靠垒”的成就感。
Lion 莱恩呀
2025/05/16
500
TCP服务器的演变过程:多进程实现一对多的TCP服务器
I/O多路复用select/poll/epoll
早期操作系统通常将进程中可创建的线程数限制在一个较低的阈值,大约几百个。因此, 操作系统会提供一些高效的方法来实现多路IO,例如Unix的select和poll。现代操作系统中,线程数已经得到了极大的提升,如NPTL线程软件包可支持数十万的线程。
WindSun
2019/09/09
1.3K0
I/O多路复用select/poll/epoll
基于UDP的C/S模型代码实现
传输层主要应用的协议模型有两种,一种是TCP协议,另外一种则是UDP协议。TCP协议在网络通信中占主导地位,绝大多数的网络通信借助TCP协议完成数据传输。但UDP也是网络通信中不可或缺的重要通信手段。
mindtechnist
2025/03/24
770
基于UDP的C/S模型代码实现
网络编程-一个简单的echo程序(0)
在上一篇《网络编程-从TCP连接的建立说起》中简单介绍了TCP连接的建立,本文暂时先抛开TCP更加详细的介绍,来看看如何实现一个简单的网络程序。
编程珠玑
2019/07/12
5700
网络编程-一个简单的echo程序(0)
SOCKET网络编程 (通俗易懂入门篇)
struct sockaddr :很多网络编程函数的出现早于IPV4协议,为了向前兼容,现在sockaddr都退化成(void *)结构了。 传递一个地址给函数,然后由函数内部再强制类型转换为所需的地址类型。
看、未来
2020/08/25
1.1K0
SOCKET网络编程 (通俗易懂入门篇)
多路 io 转接模型 select/poll
多路io转发服务器模型也是为了解决大并发多客户端场景下的问题,比多进程、多线程开销要少。多进程多线程常规情况下都是使用 accept 或 read 函数在阻塞等接收客户端发送过来的数据,而多路io模型则是提供了一个系统函数,该函数负责阻塞判断各路被监控的文件描述符是否有数据读取或写入操作,当有数据读取或写入时再让 accept 或 read 去直接处理从而不会阻塞,系统函数可能会同时返回多个有数据的文件描述符等待后面的代码处理,所以效率上要比多进程和多线程同时只在一个位置阻塞获取数据效率要高一些,下面就介绍一下多路 io 模型 select 和 poll,poll 模型较 select 模型还存在一些优势,在本文后面将介绍。
我与梦想有个约会
2023/10/20
2770
多路 io 转接模型 select/poll
socket 多进程/多线程模型实现
前文我们实现了一个 socket 最小的实现,它只允许一台终端连接到服务器进行数据通信,但这样的程序对我们来说没有什么意义,所以我们一定要实现多个客户端与一个服务端通信交互数据,这样才能真正派上用场,所以本文主要介绍了两种实现多客户端连接的方案,一种是多进程,一种是多线程,两种性能相差无几,但明显多线程在资源方面明显要比多进程消耗要少的多。
我与梦想有个约会
2023/10/20
4050
socket 多进程/多线程模型实现
205-ESP32_SDK开发-TCP服务器(select方式,支持多连接,高速高并发传输)
https://www.cnblogs.com/orlion/p/6119812.html
杨奉武
2021/12/12
1.1K0
205-ESP32_SDK开发-TCP服务器(select方式,支持多连接,高速高并发传输)
套接字函数 | socket、bind、listen、accept、connect
AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址。
mindtechnist
2025/03/04
2210
套接字函数 | socket、bind、listen、accept、connect
UNIX网络编程卷1(第三版) 客户/服务器程序示例
创建TCP套接字。在待绑定到该套接字的网际网套接字地址结构中填入通配地址(INADDR_ANY)和服务器众所周知的端口(SERV_PORT).捆绑通配地址是告诉系统:要是系统是多宿主机,我们将接受目的地址为任何本地接口的连接。listen把该套接字转成一个监听套接字。
心跳包
2020/08/31
4560
linux服务器开发三(网络编程) --二
半关闭 当TCP链接中A发送FIN请求关闭,B端回应ACK后(A端进入FIN_WAIT_2状态),B没有立即发送FIN给A时,A方处在半链接状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据。 从程序的角度,可以使用API来控制实现半连接状态。 #include <sys/socket.h> int shutdown(int sockfd, int how); sockfd: 需要关闭的socket的描述符 how: 允许为shutdown操作选择以下几种方式: SHUT
李海彬
2018/03/27
2.5K0
linux服务器开发三(网络编程) --二
2-UNIX网络编程-进阶学习前的基础知识储备
诚实点,并不是假设读的人C语言基础很差,而事实是,自己的C语言基础实在太差,要补学基础知识之后才可以进行后续的学习,惭愧!
zoujunjie202
2022/07/27
4290
2-UNIX网络编程-进阶学习前的基础知识储备
多路I/O转接服务器
多路IO转接服务器也叫做多任务IO服务器。该类服务器实现的主旨思想是,不再由应用程序自己监视客户端连接,取而代之由内核替应用程序监视文件。
mindtechnist
2024/08/08
1650
多路I/O转接服务器
UDP套接口编程
常用的UDP实现的程序:DNS域名系统,NFS网络文件系统,SNMP简单网络管理协议 ssize_t recvfrom(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr * from,socklen_t *addrlen); ssize_t sendto(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr * to,socklen_t addrlen); sock
用户1154259
2018/01/17
9890
相关推荐
【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验