🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️本博客致力于知识分享,与更多的人进行学习交流
对于常见的C/S模型,一个服务端通常需要服务多个客户端。如果使用单行的处理模型,当新的客户端请求服务端的服务时,就必须等待比它先到的客户端的请求全部完成。
因此引入多进程并发服务器模型。多进程并发服务器模型的简单流程图如下所示。父进程创建一个套接字,然后与自己的IP地址、端口号进行绑定。之后调用开始监听来自客户端的敲门,当有客户端来敲门时,accept()接收客户端的连接并创建一个新套接字用于与客户端通信。接下来调用fork()函数,当调用fork()函数时,操作系统会复制当前进程的一个副本,包括进程的代码、数据和状态等信息。如果其返回值为负数,表示创建子进程失败。否则他在父子进程中有不同的返回值:如果返回值为0,表示当前代码正在子进程中执行。如果返回值大于0,表示当前代码正在父进程中执行,返回的值是子进程的进程ID。因此可以使用if-else语句来编写子进程的处理代码。
在子进程中,先关闭从父进程中复制下来监听套接字,这个套接字在子进程中没有用了,纯属浪费资源,之后再进行与客户端的通信。而在父进程中,同理关闭accept()创建的新套接字,然后继续监听客户端的连接请求。
而同一进程的所有线程共享相同的内存空间,线程数据共享和通信更加方便,创建和管理线程的资源消耗较少,尤其是内存开销较小。由于线程间通信和进程间通信,多线程模型在处理大量任务时响应速度较快。
在多进程+多线程代码的基础上,将进行accept的进程修改为创建的线程工作,socket通信的功能放在线程工作函数内。
使用服务器测试业务:
客户端向标准输入发送小写字符串,服务端响应回复对应大写字符,"abcAS"->"ABCAS"
客户端向服务端发送关键字localtime,服务端响应回复系统时间、
服务端:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define _SERVER_IP "xxx.xxx.xxx.xxx"
- #define _PORT 8080
- #define _BACKLOG 128
- #define _SHUTDOWN 1
- #define _TRUE 1
- #define _FALSE 0
- #define _IPSIZE 16
- #define _RECVLEN 1500
-
- struct client_info
- {
- int client_fd;
- struct sockaddr_in clientAddr;
- };
-
- void sig_wait(int n)
- {
- pid_t zpid;
- while ((zpid = waitpid(-1, NULL, WNOHANG)) > 0)
- {
- printf("wait Thread Tid [0x%x] Wait Successfully,Zombie %d\n", (unsigned int)pthread_self(), zpid);
- }
- }
-
- void *thread_wait(void *arg)
- {
- // 设定信号捕捉
- pthread_detach(pthread_self());
- struct sigaction act, oact;
- act.sa_handler = sig_wait;
- act.sa_flags = 0;
- sigemptyset(&act.sa_mask);
- sigaction(SIGCHLD, &act, &oact);
- // 解除屏蔽
- sigprocmask(SIG_SETMASK, &act.sa_mask, NULL);
- printf("wait Thread [0x%x] is Waiting...\n", (unsigned int)pthread_self());
- while (1)
- sleep(1);
- pthread_exit(NULL);
- }
- void *thread_recv(void *arg)
- {
- int recvlen;
- char Result[_RECVLEN];
- char client_ip[_IPSIZE];
- time_t tp;
- char time_buf[100]; // 存放当前系统时间
- int toupper_flag;
-
- struct client_info cinfo = *((struct client_info *)arg);
- // Socket通信
- bzero(Result, sizeof(Result));
- bzero(client_ip, sizeof(client_ip));
- inet_ntop(AF_INET, &cinfo.clientAddr.sin_addr.s_addr, client_ip, _IPSIZE);
- printf("Connection From :IP[%s],PORT[%d]\n", client_ip, ntohs(cinfo.clientAddr.sin_port));
- sprintf(Result, "Hi [%s] Welcome to my TCP test server!service version 1.1.0...", client_ip);
- send(cinfo.client_fd, Result, strlen(Result), 0);
- bzero(Result, sizeof(Result));
-
- // 读取用户数据,如果用户发的是普通小写字符字符串,转换为大写,如果发送的是local关键字,响应时间
- // 持续响应,循环读写
- while ((recvlen = recv(cinfo.client_fd, Result, sizeof(Result), 0)) > 0)
- { // 处理客户端业务
- printf("Client Say:%s\n", Result);
- if (strcmp(Result, "localtime") == 0)
- {
- tp = time(NULL); // 获取时间种子
- ctime_r(&tp, time_buf);
- time_buf[strcspn(time_buf, "\n")] = '\0';
- printf("[%s]Response SysTime Successfully!\n", client_ip);
- send(cinfo.client_fd, time_buf, strlen(time_buf) + 1, 0);
- }
- else
- {
- toupper_flag = 0;
- while (recvlen > toupper_flag)
- {
- Result[toupper_flag] = toupper(Result[toupper_flag]);
- ++toupper_flag;
- }
- printf("[%s]Response Toupper Successfully!\n", client_ip);
- send(cinfo.client_fd, Result, recvlen, 0);
- }
- }
- if (recvlen == 0) // 客户端退出
- {
- close(cinfo.client_fd);
- printf("[%s] is Exiting,Kill Child\n", client_ip);
- exit(0);
- }
-
- close(cinfo.client_fd);
- }
-
- int main()
- {
-
- struct sockaddr_in serverAddr, clientAddr;
- int server_fd;
- int client_fd;
-
- // 主线程设置屏蔽
- sigset_t set, oldset;
- sigemptyset(&set);
- sigaddset(&set, SIGCHLD);
- sigprocmask(SIG_SETMASK, &set, &oldset);
- pthread_t tid;
- pthread_create(&tid, NULL, thread_wait, NULL); // 创建回收线程
-
- socklen_t Addrlen;
- serverAddr.sin_family = AF_INET;
- serverAddr.sin_port = htons(_PORT);
- serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- server_fd = socket(AF_INET, SOCK_STREAM, 0);
- bind(server_fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
- listen(server_fd, _BACKLOG);
- printf("Test TCP Server Version 1.1.0 is Running...\n");
-
- while (_SHUTDOWN)
- {
- Addrlen = sizeof(clientAddr);
- if ((client_fd = accept(server_fd, (struct sockaddr *)&clientAddr, &Addrlen)) > 0)
- {
-
- struct client_info cinfo;
- cinfo.client_fd = client_fd;
- cinfo.clientAddr = clientAddr;
- pthread_create(&tid, NULL, thread_recv, &cinfo); // 创建工作线程
- }
- else
- {
- // accept失败,测试中断
- close(server_fd);
- if (errno == EINTR)
- {
- printf("Accept ERROR Eintr...\n");
- exit(0);
- }
- }
- }
- close(server_fd);
- return 0;
- }
客户端:
- #ifndef _MYSOCK_H_
- #define _MYSOCK_H_
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int SOCKET(int domain, int type, int protocol);
- int BIND(int sockfd, struct sockaddr* addr, socklen_t addrlen);
- ssize_t RECV(int sockfd, void* buf, size_t len, int flags);
- ssize_t SEND(int sockfd, void* buf, size_t len, int flags);
- int CONNECT(int sockfd, struct sockaddr* addr, socklen_t addrlen);
- int ACCEPT(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
- int LISTEN(int sockfd, int backlog);
- char* FGETS(char* s, int size, FILE* stream);
- int SELECT(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
- struct timeval* timeout);
- int socket_init();
- int return_response(int clientfd, const char* clientip);
- //void strDeal(int *client_fd);
-
- // 全局变量声明
- char recv_buf[1024];
- char time_buf[64];
- int serverFd, clientFd;
- struct sockaddr_in clientAddr;
- fd_set set, oset;
- int client_array[1020];
- int maxfd, ready;
- socklen_t addrlen;
- char clientip[16];
- time_t tp;
- ssize_t recvlen;
- int toupper_flag;
- #define SHUTDOWN 1
- #endif
- #include "MySock.h"
-
-
- //客户端源码编写,连接服务器成功,服务器反馈信息
-
- #define _IP "xxx.xxx.xxx.xxx"
- #define _PORT 8080
- int main()
- {
- struct sockaddr_in ServerAddr;
- bzero(&ServerAddr,sizeof(ServerAddr));
- ServerAddr.sin_family=AF_INET;
- ServerAddr.sin_port=htons(_PORT);
- inet_pton(AF_INET,_IP,&ServerAddr.sin_addr.s_addr);
-
- int Myfd=SOCKET(AF_INET,SOCK_STREAM,0);
- //看需求决定是否要绑定
- char Response[1024];//存放服务端反馈信息
- ssize_t recvlen;
- bzero(Response,sizeof(Response));
- char sendbuf[1024];
-
- if((CONNECT(Myfd,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr)))==0)
- {
- while(1)
- {
- if((recvlen=RECV(Myfd,Response,sizeof(Response),0))>0)
- {
- printf("%s\n",Response);
- }
-
- printf("Please Type Some text:");//读取标准输入发送给服务端
- FGETS(sendbuf,sizeof(sendbuf),stdin);
- sendbuf[strcspn(sendbuf,"\n")]='\0';
- SEND(Myfd,sendbuf,sizeof(sendbuf),0);
- }
- }
- close(Myfd);
- printf("Client is Over\n");
- return 0;
- }