一.思路
实现一个服务器可以连接多个客户端,每当accept函数等待到客户端进行连接时 就创建一个子进程;
核心思路:让accept循环阻塞等待客户端,每当有客户端连接时就fork子进程,让子进程去和客户端进行通信,父进程用于监听并使用信号捕捉回收子进程;(子进程关闭用于监听的套接字lfd,父进程关闭用于通信的cfd)
二.多进程并发服务器代码实现:
1.利用while 和 accept 阻塞等待客户端连接,一旦有一个客户端连接上,就创建一个新的子进程执行服务逻辑。
2.子进程结束时,利用信号捕捉函数sigaction回收子进程
3.注意,当主进程阻塞在accept时,若子进程发出信号,accept会被打断(一系列慢速系统调用的特点)从而直接执行接下来的fork函数,这就会导致一个子进程刚好被回收后,又会产生一个新的子进程,为了防止这种情况,使用again和goto维护accept,防止被信号打断后产生错误。
1 #include<iostream>
2 #include<unistd.h>
3 #include<sys/socket.h>
4 #include<arpa/inet.h>
5 #include<signal.h>
6 #include<sys/wait.h>
7 using namespace std;
8
9 void huidiao(int signo)
10 {
11 pid_t pid;
12 while((pid = waitpid(-1,NULL,WNOHANG))>0)
13 {
14 cout<<pid<<endl;
15 }
16 return ;
17 }
18
19 int main()
20 {
21 struct sigaction act;
22 act.sa_handler = huidiao; sigemptyset(&act.sa_mask);
24 act.sa_flags = 0;
25 sigaction(SIGCHLD,&act,NULL);
26 //socket bind listen accept
27 int fd = socket(AF_INET,SOCK_STREAM,0);
28 struct sockaddr_in serveraddr,clientaddr;
29 serveraddr.sin_family = AF_INET;
30 serveraddr.sin_port = htons(9876);
31 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
32 bind(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
33
34 listen(fd,128);
35
36 socklen_t clientaddr_len = sizeof(clientaddr);
37 int sfd,flag;
38 while(true)
39 {
40 sfd = accept(fd,(struct sockaddr*)&clientaddr,&clientaddr_len);
41 flag = fork();
42
43 if(flag == 0)
44 { close(fd);
46 break;
47 }
48 else if(flag <0)
49 {
50 exit(1);
51 }
52 else if(flag>0)
53 {
54 close(sfd);
55 continue;
56 }
57 }
58
59 if(flag == 0)
60 {
61 for(;;)
62 {
63 char buf[1024];
64 int n = read(sfd,buf,sizeof(buf));
65 if(n==0)
66 { close(sfd);
68 cout<<"jieshu"<<endl;
69 exit(0);
70 }
71 for(int i = 0;i<n;i++)
72 {
73 buf[i] = toupper(buf[i]);
74 }
75 write(STDOUT_FILENO,buf,n);
76 write(sfd,buf,n);
77 }
78 }
79 return 0;
80 }
三. 多线程并发服务器代码实现:
1.#include <stdio.h>
2.#include <string.h>
3.#include <arpa/inet.h>
4.#include <pthread.h>
5.#include <ctype.h>
6.#include <unistd.h>
7.#include <fcntl.h>
8.
9.#include "wrap.h"
10.
11.#define MAXLINE 8192
12.#define SERV_PORT 8000
13.
14.struct s_info { //定义一个结构体, 将地址结构跟cfd捆绑
15. struct sockaddr_in cliaddr;
16. int connfd;
17.};
18.
19.void *do_work(void *arg)
20.{
21. int n,i;
22. struct s_info *ts = (struct s_info*)arg;
23. char buf[MAXLINE];
24. char str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16 可用"[+d"查看
25.
26. while (1) {
27. n = Read(ts->connfd, buf, MAXLINE); //读客户端
28. if (n == 0) {
29. printf("the client %d closed...\n", ts->connfd);
30. break; //跳出循环,关闭cfd
31. }
32. printf("received from %s at PORT %d\n",
33. inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
34. ntohs((*ts).cliaddr.sin_port)); //打印客户端信息(IP/PORT)
35.
36. for (i = 0; i < n; i++)
37. buf[i] = toupper(buf[i]); //小写-->大写
38.
39. Write(STDOUT_FILENO, buf, n); //写出至屏幕
40. Write(ts->connfd, buf, n); //回写给客户端
41. }
42. Close(ts->connfd);
43.
44. return (void *)0;
45.}
46.
47.int main(void)
48.{
49. struct sockaddr_in servaddr, cliaddr;
50. socklen_t cliaddr_len;
51. int listenfd, connfd;
52. pthread_t tid;
53.
54. struct s_info ts[256]; //创建结构体数组.
55. int i = 0;
56.
57. listenfd = Socket(AF_INET, SOCK_STREAM, 0); //创建一个socket, 得到lfd
58.
59. bzero(&servaddr, sizeof(servaddr)); //地址结构清零
60. servaddr.sin_family = AF_INET;
61. servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本地任意IP
62. servaddr.sin_port = htons(SERV_PORT); //指定端口号
63.
64. Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //绑定
65.
66. Listen(listenfd, 128); //设置同一时刻链接服务器上限数
67.
68. printf("Accepting client connect ...\n");
69.
70. while (1) {
71. cliaddr_len = sizeof(cliaddr);
72. connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //阻塞监听客户端链接请求
73. ts[i].cliaddr = cliaddr;
74. ts[i].connfd = connfd;
75.
76. pthread_create(&tid, NULL, do_work, (void*)&ts[i]);
77. pthread_detach(tid); //子线程分离,防止僵线程产生.
78. i++;
79. }
80.
81. return 0;
82.}
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » [Linux网络编程]04-多进程/多线程并发服务器思路分析及实现(进程,信号,socket,线程...)
发表评论 取消回复