计算机网络编程 | 并发服务器代码实现(多进程/多线程)

简介: 计算机网络编程 | 并发服务器代码实现(多进程/多线程)

什么是并发服务器

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

多进程并发服务器

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

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

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

多线程并发服务器

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

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

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

选择适合的并发模型

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

  • 如果稳定性和容错性是首要考虑因素,多进程并发服务器可能是更好的选择。每个子进程的独立性可以有效地隔离错误,提高服务器的可靠性。
  • 如果服务器需要处理大量的并发连接并需要更高的性能和资源利用率,多线程并发服务器可能更适合。线程的创建和切换开销相对较小,可以更高效地处理并发请求。
  • 如果同时需要稳定性和性能,可以考虑使用混合模型,即在每个进程中创建多个线程,以实现更好的负载平衡和资源利用率。

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

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

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

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

  • 父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符);
  • 系统内创建进程个数(与内存大小相关);
  • 进程创建过多是否降低整体服务性能(进程调度);

server

/* 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
void do_sigchild(int num)
{
  while (waitpid(0, NULL, WNOHANG) > 0)
    ;
}
int main(void)
{
  struct sockaddr_in servaddr, cliaddr;
  socklen_t cliaddr_len;
  int listenfd, connfd;
  char buf[MAXLINE];
  char str[INET_ADDRSTRLEN];
  int i, n;
  pid_t pid;
  struct sigaction newact;
  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);
      return 0;
    } else if (pid > 0) {
      Close(connfd);
    } else
      perr_exit("fork");
  }
  Close(listenfd);
  return 0;
}

client

/* 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
int main(int argc, char *argv[])
{
  struct sockaddr_in servaddr;
  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);
  return 0;
}

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

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

  • 调整进程内最大文件描述符上限;
  • 线程如有共享数据,考虑线程同步;
  • 服务于客户端线程退出时,退出处理(退出值,分离态);
  • 系统负载,随着链接客户端增加,导致其它线程不能及时得到CPU;

server

/* 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
struct s_info {
  struct sockaddr_in cliaddr;
  int connfd;
};
void *do_work(void *arg)
{
  int n,i;
  struct s_info *ts = (struct s_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);
}
int main(void)
{
  struct sockaddr_in servaddr, cliaddr;
  socklen_t cliaddr_len;
  int listenfd, connfd;
  int i = 0;
  pthread_t tid;
  struct s_info ts[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++;
  }
  return 0;
}

client

/* 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
int main(int argc, char *argv[])
{
  struct sockaddr_in servaddr;
  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);
  return 0;
}

618图书推荐

书籍是知识的海洋,计算机好书推荐

🔥🔥🔥618,清华社 IT BOOK 多得图书活动开始啦!活动时间为2023 年6 月7 日至6 月18 日,清华社为您精选多款高分好书,涵盖了 C++、Java、Python、前端、后端、数据库、算法与机器学习等多个IT 开发领域,适合不同层次的读者。全场5 折,扫码领券更有优惠哦!快来京东点击链接 IT BOOK多得查看详情吧!



相关文章
|
3天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略
|
17天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
1月前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0
|
1月前
|
消息中间件 存储 算法
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
83 0
|
1月前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
70 0
|
4天前
|
Java API 调度
[Java并发基础]多进程编程
[Java并发基础]多进程编程
|
4天前
|
Java 数据库连接 数据处理
Python从入门到精通:3.1.2多线程与多进程编程
Python从入门到精通:3.1.2多线程与多进程编程
|
9天前
|
安全 Java
深入理解 Java 多线程和并发工具类
【4月更文挑战第19天】本文探讨了Java多线程和并发工具类在实现高性能应用程序中的关键作用。通过继承`Thread`或实现`Runnable`创建线程,利用`Executors`管理线程池,以及使用`Semaphore`、`CountDownLatch`和`CyclicBarrier`进行线程同步。保证线程安全、实现线程协作和性能调优(如设置线程池大小、避免不必要同步)是重要环节。理解并恰当运用这些工具能提升程序效率和可靠性。
|
11天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
28 0
|
11天前
|
Java 开发者
Java中多线程并发控制的实现与优化
【4月更文挑战第17天】 在现代软件开发中,多线程编程已成为提升应用性能和响应能力的关键手段。特别是在Java语言中,由于其平台无关性和强大的运行时环境,多线程技术的应用尤为广泛。本文将深入探讨Java多线程的并发控制机制,包括基本的同步方法、死锁问题以及高级并发工具如java.util.concurrent包的使用。通过分析多线程环境下的竞态条件、资源争夺和线程协调问题,我们提出了一系列实现和优化策略,旨在帮助开发者构建更加健壮、高效的多线程应用。
7 0