一、多进程并发服务器
实现一个服务器可以连接多个客户端,每当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 9527void 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];//4096struct 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 8000struct 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, 得到lfdbzero(&servaddr, sizeof(servaddr)); //地址结构清零servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本地任意IPservaddr.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;
}
执行输出如下: