赞
踩
一、多进程并发服务器
实现一个服务器可以连接多个客户端,每当accept函数等待到客户端进行连接时 就创建一个子进程
思路分析:
核心思路:让accept循环阻塞等待客户端,每当有客户端连接时就fork子进程,让子进程去和客户端进行通信,父进程用于监听并使用信号捕捉回收子进程(子进程关闭用于监听的套接字lfd,父进程关闭用于通信的cfd)
服务端server.c代码如下:
- #include<stdio.h>
- #include<stdlib.h>
- #include<arpa/inet.h>
- #include<ctype.h>
- #include<sys/socket.h>
- #include<unistd.h>
- #include<string.h>
- #include<strings.h>
- #include<errno.h>
- #include<pthread.h>
- #include<signal.h>
- #include<sys/wait.h>
-
- #define SERV_PORT 9527
-
- void sys_err(const char *str)
- {
- perror(str);
- exit(1);
- }
-
- void catch_child(int signum)
- {
- while(waitpid(0,NULL,WNOHANG) > 0);
- return ;
- }
-
- int main(int argc,char *argv[])
- {
- int lfd = 0,cfd = 0;
- pid_t pid;
- int ret;
- char buf[BUFSIZ],client_IP[1024];//4096
-
- struct sockaddr_in serv_addr,clit_addr;
- socklen_t clit_addr_len;
-
- bzero(&serv_addr,sizeof(serv_addr));//将地址结构清零
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_port = htons(SERV_PORT);
- serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- lfd = socket(AF_INET,SOCK_STREAM,0);
- if (lfd == -1)
- {
- sys_err("socket errno");
- }
-
- bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
-
- listen(lfd,128);
-
- clit_addr_len = sizeof(clit_addr);
-
- while(1)
- {
- cfd = accept(lfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
- if (cfd == -1)
- {
- sys_err("accept error");
- }
- printf("client ip is:%s port:%d\n",inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),ntohs(clit_addr.sin_port));
-
- pid = fork();
- if (pid < 0)
- {
- sys_err("fork error");
- }
- else if (pid == 0)
- {
- close(lfd);
- break;
- }
- else
- {
- struct sigaction act;
-
- act.sa_handler = catch_child;
- sigemptyset(&(act.sa_mask));
- act.sa_flags = 0;
-
- ret = sigaction(SIGCHLD,&act,NULL);
- if (ret != 0)
- {
- sys_err("sigaction error");
- }
- close(cfd);
- continue;
- }
- }
-
- if (pid == 0)
- {
- for(;;)
- {
- ret = read(cfd,buf,sizeof(buf));
- if (ret == 0)
- {
- close(cfd);
- exit(1);
- }
-
- for (int i = 0;i<ret;i++)
- {
- buf[i] = toupper(buf[i]);
- }
-
- write(cfd,buf,ret);
- write(STDOUT_FILENO,buf,ret);
- }
- }
-
- return 0;
- }
一个服务端三个客户端执行多进程服务器并发输出如下:
补充:
二、多线程并发服务器:
思路分析:
核心思路:设置监听循环由主线程阻塞等待客户端连接,每当接受到新的客户端连接时,主线程会创建一个新的子线程来处理与该客户端的通信,而主线程继续执行监听循环等待其他客户端(主线程专注于接受新连接,子线程专注于处理客户端请求)
服务端server.c代码如下:
- #include <stdio.h>
- #include <string.h>
- #include <arpa/inet.h>
- #include <pthread.h>
- #include <ctype.h>
- #include <unistd.h>
- #include <fcntl.h>
-
- #include "wrap.h"
-
- #define MAXLINE 8192
- #define SERV_PORT 8000
-
- struct s_info { //定义一个结构体, 将地址结构跟cfd捆绑
- 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]; //#define INET_ADDRSTRLEN 16 可用"[+d"查看
-
- while (1) {
- n = Read(ts->connfd, buf, MAXLINE); //读客户端
- if (n == 0) {
- printf("the client %d closed...\n", ts->connfd);
- break; //跳出循环,关闭cfd
- }
- printf("received from %s at PORT %d\n",
- inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
- ntohs((*ts).cliaddr.sin_port)); //打印客户端信息(IP/PORT)
-
- for (i = 0; i < n; i++)
- buf[i] = toupper(buf[i]); //小写-->大写
-
- Write(STDOUT_FILENO, buf, n); //写出至屏幕
- Write(ts->connfd, buf, n); //回写给客户端
- }
- Close(ts->connfd);
-
- return (void *)0; //pthread_exit(0);
- }
-
- int main(void)
- {
- struct sockaddr_in servaddr, cliaddr;
- socklen_t cliaddr_len;
- int listenfd, connfd;
- pthread_t tid;
-
- struct s_info ts[256]; //创建结构体数组.
- int i = 0;
-
- listenfd = Socket(AF_INET, SOCK_STREAM, 0); //创建一个socket, 得到lfd
-
- bzero(&servaddr, sizeof(servaddr)); //地址结构清零
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本地任意IP
- servaddr.sin_port = htons(SERV_PORT); //指定端口号
-
- Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //绑定
-
- Listen(listenfd, 128); //设置同一时刻链接服务器上限数
-
- printf("Accepting client connect ...\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(&tid, NULL, do_work, (void*)&ts[i]);//表结构体变量作为参数传递到子线程
- pthread_detach(tid); //子线程分离,防止僵线程产生.
- i++;
- }
-
- return 0;
- }
执行输出如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。