服务器线程并发和进程并发

进程和线程的使用在前面博文已经讲述完毕,在完成一个最简单的服务器之后,就是要考虑下如何实现并发服务器了。

要实现服务的并发,只能通过进程和线程两种方式。

之前提到过listen_fd和connect_fd,listen用于监听是否有客户端连接,维护两个fd队列,没完成握手的和完成就绪的。

connect从就绪队列取描述符,这个connect_fd描述符将用于数据通信,所以要实现并发,就是将connect_fd分发到线程或进程上,由他们去独立完成通信。

在实际并发服务器应用场合,在IO层大多通过两个地方来提高代码效率,一个是描述符处理,一个是线程/进程调度处理。

下图简单描述了并发服务器的原理:

在处理IO时,会用到IO复用技术提高效率,在线程/进程分配时,会先构造线程池或进程池,并以某种方式调度,这些在后续博文详细描述。

下面是并发实现的简单代码,利用线程和进程实现服务器的并发。

进程实现:

  1 /* File Name: server.c */
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <string.h>
  5 #include <unistd.h>
  6 #include <sys/wait.h>
  7 #include <errno.h>
  8 #include <sys/types.h>
  9 #include <sys/socket.h>
 10 #include <sys/unistd.h>
 11 #include <netinet/in.h>
 12
 13 const int DEFAULT_PORT = 2500;
 14 const int BUFFSIZE = 1024;
 15 const int MAXLINK = 10;
 16
 17 class sigOp
 18 {
 19 public:
 20      void addSigProcess(int sig,void (*func)(int));
 21     void sendSig(const int sig, const int pid);
 22 };
 23
 24 void sigOp::addSigProcess(int sig,void (*func)(int))
 25 {
 26     struct sigaction stuSig;
 27     memset(&stuSig, ‘\0‘, sizeof(stuSig));
 28     stuSig.sa_handler = func;
 29     stuSig.sa_flags |= SA_RESTART;
 30     sigfillset(&stuSig.sa_mask);
 31     sigaction(sig, &stuSig, NULL);
 32 }
 33
 34 void sigOp::sendSig(const int sig, const int pid)
 35 {
 36     kill(pid, sig);
 37 }
 38
 39 void waitchlid(int sig)
 40 {
 41     pid_t pid;
 42     int stat;
 43     while((pid = waitpid(-1, &stat, WNOHANG)) > 0);
 44 }
 45
 46 int main(int argc, char** argv)
 47 {
 48     int    socket_fd, connect_fd;
 49     struct sockaddr_in servaddr;
 50     char buff[BUFFSIZE];
 51
 52     if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
 53     {
 54         printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
 55         exit(0);
 56     }
 57
 58     memset(&servaddr, 0, sizeof(servaddr));
 59     servaddr.sin_family = AF_INET;
 60     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 61     servaddr.sin_port = htons(DEFAULT_PORT);
 62
 63     if (bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
 64     {
 65         printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
 66         exit(0);
 67     }
 68
 69     if (listen(socket_fd, MAXLINK) == -1)
 70     {
 71         exit(0);
 72     }
 73
 74     sigOp sig;
 75     sig.addSigProcess(SIGCHLD, waitchlid);//回收进程
 76
 77     while(1)
 78     {
 79         if ((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1)
 80         {
 81             break;
 82         }
 83         else
 84         {
 85             if (0 == fork())
 86             {
 87                 close(socket_fd); //关闭监听描述符
 88                 while(1)
 89                 {
 90                     memset(&buff,‘\0‘, sizeof(buff));
 91                     recv(connect_fd, buff, BUFFSIZE - 1, 0);
 92                     send(connect_fd, buff, BUFFSIZE - 1, 0);
 93                     printf("recv msg from client: %s\n", buff);
 94                 }
 95                 close(connect_fd);
 96                 exit(0);
 97             }
 98             close(connect_fd);//父进程关闭连接描述符
 99         }
100     }
101     close(connect_fd);
102     close(socket_fd);
103 }  

之前提到过listen描述符和connect描述符是完全独立的,关闭都不会影响另一个描述符工作。所以在代码中,父子进程都会关闭不需要的描述符。

测试结果如下:

ps -aux查看系统进程,可以看到三个进程,一个是主进程用于listen监听,两个子进程进行通信。

下面是线程并发实现:

 1 /* File Name: server.c */
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <unistd.h>
 6 #include <pthread.h>
 7 #include <errno.h>
 8 #include <sys/types.h>
 9 #include <sys/socket.h>
10 #include <sys/unistd.h>
11 #include <netinet/in.h>
12
13 const int DEFAULT_PORT = 2500;
14 const int BUFFSIZE = 1024;
15 const int MAXLINK = 10;
16
17 void* run(void* pConnect_fd)
18 {
19     char buff[BUFFSIZE];
20     int *connect_fd = reinterpret_cast<int *>(pConnect_fd);
21     pthread_detach(pthread_self());
22     while(1)
23     {
24         memset(&buff,‘\0‘, sizeof(buff));
25         recv(*connect_fd, buff, BUFFSIZE - 1, 0);
26         send(*connect_fd, buff, BUFFSIZE - 1, 0);
27         printf("recv msg from client: %s\n", buff);
28     }
29 }
30
31 int main(int argc, char** argv)
32 {
33     int    socket_fd, connect_fd;
34     struct sockaddr_in servaddr;
35
36     if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
37     {
38         printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
39         exit(0);
40     }
41
42     memset(&servaddr, 0, sizeof(servaddr));
43     servaddr.sin_family = AF_INET;
44     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
45     servaddr.sin_port = htons(DEFAULT_PORT);
46
47     if (bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
48     {
49         printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
50         exit(0);
51     }
52
53     if (listen(socket_fd, MAXLINK) == -1)
54     {
55         exit(0);
56     }
57
58     while(1)
59     {
60         if ((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1)
61         {
62             break;
63         }
64         else    //accept获取到描述符创建线程
65         {
66             pthread_t _Tread;
67             pthread_create(&_Tread, NULL, run, &connect_fd);//传参为连接描述符
68         }
69     }
70     close(connect_fd);
71     close(socket_fd);
72 }  

测试结果如下:

效果和进程一样,执行netstat查看tcp状态

两组连接相互通信。

线程并发和进程并发各有优劣,目前大多服务器还是用线程进行并发的,进程要对父进程进行拷贝,资源消耗大,但相互直接资源互不影响,线程效率高但是要注意锁的使用,一个线程可能会影响整个服务器的运行。

详细优缺点详细可参考:http://blog.chinaunix.net/uid-20556054-id-3067672.html

时间: 2024-08-01 16:40:42

服务器线程并发和进程并发的相关文章

进程与线程、并行与并发的理解

进程与线程 1.定义 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 2.关系 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行. 相对进程而言,线程是一个更加接近于执

Java并发编程--进程与线程

进程:百度百科说"进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.",维基百科说"是计算机中已运行程序的实体.进程本身不会运行,是线程的容器." 线程:百度百科说"线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元",维基百科说"是操作系统能够进行运算调度的最小单位.它被包涵在进程之中,是进程中的实际运作单位

并发编程 --- 进程,线程

目录 进程 线程 进程 1.进程互斥锁 异步可以让多个任务在几个进程中并发处理,他们之间没有运行顺序,一旦开启也不受我们的控制,尽管并发编程让我们更加充分的利用IO资源,但是当多个进程使用同一份资源的时候,就会引发数据安全或顺序混乱的问题 import json import time from multiprocessing import Process from multiprocessing import Lock # 查看余票 def search(user): # 打开data文件查看

多线程、进程、并发、并行、同步、异步、伪并发、真并发

进程.线程 1.进程 一个程序,可以独立运行的一段程序.系统对它进行资源分配和调度. 2.线程 进程的基本单位,对它进行cpu分配和调度.只拥有一点在运行中必不可少的资源(寄存器,栈,程序计数器) 3.线程与进程的联系与区别 联系: (1)线程是指进程内的一个执行单元,一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程). 但是存在 DOS 这样的单进程(而且无线程概念)系统. (2)资源分配给进程,同一进程的所有线程共享该进程的所有资源,线程自己基本上不拥有系

线程的理解和并发解决方案

一,并发和并行的区别 并行:同时做某些事,可以互不干扰的同一时刻做几件事(不一定同时).并行:同一时刻做某些事,但是强调同一时刻做了几件事. 并发的解决方案 1,队列,缓冲区假设只有一个窗口,陆续进入食堂打饭,排队的人就是队列,先进先出,解决资源使用问题.排成的队列,就是缓冲区. 2,争抢 只开一个窗户,可能没有秩序,谁的拳头大是吃饭,拳头大的抢到窗口,打完饭离开,后面的继续争抢再到下一个拳头大的.不管怎么争抢,总会只有一个人占据窗口,这个时候这个窗口只为这个人服务,不再为其他人提供服务,这就是

netstat--查看服务器[有效]连接数--统计端口并发数--access.log分析

简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Memberships) 等等. 输出信息含义 执行netstat后,其输出结果为 ? ? Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 2 210.

IIS服务器能支持10万并发

服务器能支持10万并发由于一个项目的访问量越来越大,从原来的几百个,增加到现在50多万个每天,所以阿里的服务器也扛不住了,于是就想办法,查了N多资料,总结了以下方法,才解决这问题,每天一共访问量50多万,并不是同时访问,所以,提高并发数才是关键. 由于网站一再打不开,按照排除原因来看,数据库连接.进程池满了.iis并发数太高,我能想到的就只有这三种情况,于是就一个一个排除,当项目网站打不开时,同服务器其他网站都正常,内存不到30%,CUP不到20%,怎么可能打不开呢,于是我先重启数据库,发现仍然

[高并发]Java高并发编程系列开山篇--线程实现

ava是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相同的内存空间进行并发读写操作.这可能是在单线程程序中从来不会遇到的问题.其中的一些错误也未必会在单CPU机器上出现,因为两个线程从来不会得到真正的并行执行.然而,更现代的计算机伴随着多核CPU的出现,也就意味着不同的线程能被不同的CPU核得到真正意义的并行执行. 那么,要开始Java并发之路,就要开始

服务器nginx 用shell 统计并发

# netstat -an| grep ':80' | awk '/tcp/{a[$NF]++}END{for (i in a) print i,a[i]}' # echo "当前并发数:`netstat -ant | grep :80 | awk '{print $5}' | awk -F":" '{print $1}'| sort | uniq -c | sort -r -n | awk 'BEGIN{total=0}{total+=$1}END{print total}