赞
踩
目录
首先是理论支撑
- pid = fork();
- if (pid < 0) {
- ERR_EXIT("fork");
- }
- if (pid) {
- close(connfd); //父进程只进行监听
- } else {
- close(listenfd); //子进程不需要监听
- ..... //服务逻辑代码
- }
还是上文连接所实现的过程, 服务端将客户端发来的字符转大写写回.
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <strings.h>
- #include <signal.h>
-
- #define SERVE_PORT 12345
- #define ERR_EXIT(m) \
- do { perror(m); exit(EXIT_FAILURE); } while (0)
- typedef struct sockaddr SA;
- int listenfd; //设置全局监听套接字, 方便关闭
- void handle(int signo) {
- fprintf(stdout, "ByeBye!\n");
- close(listenfd);
- exit(EXIT_SUCCESS);
- }
-
- int main() {
- signal(SIGCHLD, SIG_IGN);
- signal(SIGINT, handle);
- int listenfd, connfd, pid;
- struct sockaddr_in serveAdd, clientAdd;
- socklen_t clientAdd_len;
- char ipbuff[256];
- //创建套接字
- if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- ERR_EXIT("socket"); //协议家族 服务类型(套接字类型), 协议弃用(0)
- }
- //确定服务端地址簇
- bzero(&serveAdd, sizeof(serveAdd)); //清0
- serveAdd.sin_family = AF_INET;
- serveAdd.sin_port = htons(SERVE_PORT);
- serveAdd.sin_addr.s_addr = htonl(INADDR_ANY); //注意转网络字节序
- //bind 端口 地址信息
- if (bind(listenfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
- ERR_EXIT("bind");
- }
- //开始监听..
- if (listen(listenfd, 3) == -1) {
- ERR_EXIT("listen");
- }
- printf("Accepting connections..\n");
- //循环不断的接收客户的连接请求进行服务
- while (1) {
- clientAdd_len = sizeof(clientAdd);
- if ((connfd = accept(listenfd, (SA*)&clientAdd, &clientAdd_len)) == -1) {
- ERR_EXIT("accept");
- }
- printf("recieve connection from ip is %s and port is %d\n",
- inet_ntop(AF_INET, &clientAdd.sin_addr, ipbuff, sizeof(ipbuff)),
- ntohs(clientAdd.sin_port));
- pid = fork();
- if (pid < 0) {
- ERR_EXIT("fork");
- }
- if (pid) {
- close(connfd); //父进程只进行监听
- } else {
- //服务
- close(listenfd); //子进程不需要监听
- while (1) {
- char buff[1024] = {0};
- int i;
- int n = read(connfd, buff, sizeof(buff)); //读数据
- if (n == -1) {
- ERR_EXIT("read");
- }
- if (n == 0) { //说明客户端主动断开连接
- break;
- }
- //处理数据, 简单的小写字符转大写
- for (i = 0; i < n; ++i) {
- buff[i] = toupper(buff[i]);
- }
- //写回
- write(connfd, buff, n);
- }
- fprintf(stdout, "ip %s and port is %d interrupt connfd\n",
- ipbuff, ntohs(clientAdd.sin_port));
- close(connfd);
- exit(EXIT_SUCCESS);//子进程完成通信(服务)断开
- }
- }
- return 0;
- }
首先还是理论支撑
- //线程逻辑代码:
- void* Routine(void* arg) {
- pthread_detach(pthread_self());
- int connfd = (int)arg;
- //网络通信服务端服务逻辑。。。
- return (void*)0;
- }
- //循环不断的接收客户的连接请求进行服务
- while (1) {
- clientAdd_len = sizeof(clientAdd);
- if ((connfd = accept(listenfd, (SA*)&clientAdd, &clientAdd_len)) == -1) {
- ERR_EXIT("accept");
- }
- printf("recieve connection from ip is %s and port is %d\n",
- inet_ntop(AF_INET, &clientAdd.sin_addr, ipbuff, sizeof(ipbuff)),
- ntohs(clientAdd.sin_port));
- //创建一个子线程, 来一个新的连接就创建一个
- pthread_create(&tid, NULL, Routine, (void*)connfd);
- }
- [tangyujie@VM-4-9-centos Serve]$ cat server.c
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <strings.h>
- #include <signal.h>
- #include <pthread.h>
-
- #define SERVE_PORT 12345
- #define ERR_EXIT(m) \
- do { perror(m); exit(EXIT_FAILURE); } while (0)
- typedef struct sockaddr SA;
- int listenfd; //设置全局监听套接字, 方便关闭
- void handle(int signo) {
- fprintf(stdout, "ByeBye!\n");
- close(listenfd);
- exit(EXIT_SUCCESS);
- }
-
- void* Routine(void* arg) {
- pthread_detach(pthread_self()); //线程结束自动回收资源
- int connfd = (int)arg; //先将参数强转回去
- //服务
- while (1) {
- char buff[1024] = {0};
- int i;
- int n = read(connfd, buff, sizeof(buff));//读数据
- if (n == -1) {
- ERR_EXIT("read");
- }
- if (n == 0) { //说明客户端断开连接了
- break;
- }
- //处理数据, 简单的小写字符转大写
- for (i = 0; i < n; ++i) {
- buff[i] = toupper(buff[i]);
- }
- write(connfd, buff, n); //写回
- }
- fprintf(stdout, "interrupt connfd end\n");
- close(connfd); //服务结束断开连接
- return (void*)0;
- }
-
- int main() {
- signal(SIGCHLD, SIG_IGN);
- signal(SIGINT, handle);
- int listenfd, connfd;
- pthread_t tid;
- struct sockaddr_in serveAdd, clientAdd;
- socklen_t clientAdd_len;
- char ipbuff[256];
- //创建套接字
- if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- ERR_EXIT("socket"); //协议家族 服务类型(套接字类型), 协议弃用(0)
- }
- //确定服务端地址簇
- bzero(&serveAdd, sizeof(serveAdd)); //清0
- serveAdd.sin_family = AF_INET;
- serveAdd.sin_port = htons(SERVE_PORT);
- serveAdd.sin_addr.s_addr = htonl(INADDR_ANY); //注意转网络字节序
- //bind 端口 地址信息
- if (bind(listenfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
- ERR_EXIT("bind");
- }
- //开始监听..
- if (listen(listenfd, 3) == -1) {
- ERR_EXIT("listen");
- }
- printf("Accepting connections..\n");
- //循环不断的接收客户的连接请求进行服务
- while (1) {
- clientAdd_len = sizeof(clientAdd);
- if ((connfd = accept(listenfd, (SA*)&clientAdd, &clientAdd_len)) == -1) {
- ERR_EXIT("accept");
- }
- printf("recieve connection from ip is %s and port is %d\n",
- inet_ntop(AF_INET, &clientAdd.sin_addr, ipbuff, sizeof(ipbuff)),
- ntohs(clientAdd.sin_port));
- //创建一个子线程, 来一个新的连接就创建一个
- pthread_create(&tid, NULL, Routine, (void*)connfd);
- }
- return 0;
- }
线程池理论支撑 :附上连接一份, 学完应该足以支撑, 上一份代码是 C++的, 但是所用逻辑理论等是相通的.https://blog.csdn.net/weixin_53695360/article/details/122745816?spm=1001.2014.3001.5502
函数刨析
- [tangyujie@VM-4-9-centos Serve]$ cat server.c
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <strings.h>
- #include <signal.h>
- #include <pthread.h>
-
- #define SERVE_PORT 12345
- #define ERR_EXIT(m) \
- do { perror(m); exit(EXIT_FAILURE); } while (0)
- typedef struct sockaddr SA;
- int listenfd; //设置全局监听套接字, 方便关闭
-
- //设置任务结构体.....
- typedef struct Task {
- int connfd; //任务需要晓得哈是哪个做
- struct Task* next;
- } Task;
-
- typedef struct TaskQueue {
- Task* front;
- Task* tail;
- pthread_mutex_t lock;
- pthread_cond_t cond;
- } TaskQueue;
-
- TaskQueue* tp;
-
- void ClearTask(Task* head) {
- Task* p = head, *q;
- while (p) {
- q = p->next;
- free(p);
- p = q;
- }
- }
-
- void DestroyTaskQueue(TaskQueue* tp) {
- ClearTask(tp->front);
- pthread_mutex_destroy(&tp->lock);
- pthread_cond_destroy(&tp->cond);
- }
- void handle(int signo) {
- fprintf(stdout, "ByeBye!\n");
- DestroyTaskQueue(tp);
- close(listenfd);
- exit(EXIT_SUCCESS);
- }
-
- TaskQueue* InitTaskQueue() {
- TaskQueue* tp = (TaskQueue*)malloc(sizeof(TaskQueue));
- tp->front = tp->tail = NULL;
- pthread_mutex_init(&tp->lock, NULL);
- pthread_cond_init(&tp->cond, NULL);
- return tp;
- }
-
- void Lock(TaskQueue* tp) {
- pthread_mutex_lock(&tp->lock);
- }
-
- void Unlock(TaskQueue* tp) {
- pthread_mutex_unlock(&tp->lock);
- }
-
- void WakeUp(TaskQueue* tp) {
- pthread_cond_signal(&tp->cond);
- }
-
- void Wait(TaskQueue* tp) {
- pthread_cond_wait(&tp->cond, &tp->lock);
- }
-
- Task* GetNewTask(int connfd) {
- Task* newTask = (Task*)malloc(sizeof(Task));
- newTask->connfd = connfd;
- newTask->next = NULL;
- return newTask;
- }
-
- int IsEmpty(TaskQueue* tp) {
- return tp->front == NULL;
- }
-
- void Push(TaskQueue* tp, Task* task) {
- Lock(tp); //临界资源操作需要原子操作, 锁之间
- if (IsEmpty(tp)) {
- tp->front = task;
- tp->tail = task;
- WakeUp(tp); //唤醒通知有任务了
- Unlock(tp);
- return ;
- }
- tp->tail->next = task;
- tp->tail = task;
- WakeUp(tp); //唤醒通知有任务了
- Unlock(tp);
- }
-
- Task* Pop(TaskQueue* tp) {
- Lock(tp);
- Task* poptask;
- while (IsEmpty(tp)) { //没有任务, 就一直等待生产
- Wait(tp); //循环是为了避免伪唤醒. 唤醒多个线程
- } //但是有些线程 还要继续Wait, 所以循环
- poptask = tp->front;
- tp->front = tp->front->next;
- if (tp->front == NULL) tp->tail = NULL;
- Unlock(tp);
- return poptask;
- }
-
-
- void* Routine(void* arg) {
- TaskQueue* tp = (TaskQueue*)arg;
- while (1) { //不断的尝试Pop获取任务进行处理
- int connfd = Pop(tp)->connfd; //Pop任务后获取connfd
- //服务
- while (1) {
- char buff[1024] = {0};
- int i;
- int n = read(connfd, buff, sizeof(buff));//读数据
- if (n == -1) {
- ERR_EXIT("read");
- }
- if (n == 0) { //说明客户端断开连接了
- break;
- }
- //处理数据, 简单的小写字符转大写
- for (i = 0; i < n; ++i) {
- buff[i] = toupper(buff[i]);
- }
- write(connfd, buff, n); //写回
- }
- fprintf(stdout, "interrupt connfd end\n");
- close(connfd); //服务结束断开连接
- }
- return (void*)0;
- }
-
- void InitPool(TaskQueue* tp, int n) {
- pthread_t tid;
- int i;
- for (i = 0; i < n; ++i) {
- pthread_create(&tid, NULL, Routine, (void*)tp); //此处传入tp 需要拿取任务
- pthread_detach(tid); //线程结束自动回收线程资源
- }
- }
-
- int main() {
- signal(SIGCHLD, SIG_IGN);
- signal(SIGINT, handle);
- tp = InitTaskQueue();//初始化任务队列
- InitPool(tp, 3); //一开始就开启消费者多线程
- int listenfd, connfd;
- pthread_t tid;
- struct sockaddr_in serveAdd, clientAdd;
- socklen_t clientAdd_len;
- char ipbuff[256];
- //创建套接字
- if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- ERR_EXIT("socket"); //协议家族 服务类型(套接字类型), 协议弃用(0)
- }
- //确定服务端地址簇
- bzero(&serveAdd, sizeof(serveAdd)); //清0
- serveAdd.sin_family = AF_INET;
- serveAdd.sin_port = htons(SERVE_PORT);
- serveAdd.sin_addr.s_addr = htonl(INADDR_ANY); //注意转网络字节序
- //bind 端口 地址信息
- if (bind(listenfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
- ERR_EXIT("bind");
- }
- //开始监听..
- if (listen(listenfd, 3) == -1) {
- ERR_EXIT("listen");
- }
- printf("Accepting connections..\n");
- //循环不断的接收客户的连接请求进行服务
- while (1) {
- clientAdd_len = sizeof(clientAdd);
- if ((connfd = accept(listenfd, (SA*)&clientAdd, &clientAdd_len)) == -1) {
- ERR_EXIT("accept");
- }
- printf("recieve connection from ip is %s and port is %d\n",
- inet_ntop(AF_INET, &clientAdd.sin_addr, ipbuff, sizeof(ipbuff)),
- ntohs(clientAdd.sin_port));
- Task* newtask = GetNewTask(connfd);
- Push(tp, newtask);
- }
- return 0;
- }
然后是上述所有服务端均可通用的客户端代码:
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <strings.h>
- #include <signal.h>
- #include <string.h>
-
- #define SERVE_PORT 12345 //端口号
- #define ERR_EXIT(m)\
- do { perror(m); exit(EXIT_FAILURE); } while (0) //错误处理
- typedef struct sockaddr SA;
- int sockfd; //设置全局, 方便关闭
- void handle(int signo) {
- fprintf(stdout, "ByeBye!\n");
- close(sockfd);
- exit(EXIT_SUCCESS);
- }
- int main(int argc, char* argv[]) {
- if (argc != 2) {
- fprintf(stderr, "%s <ip>", argv[0]);
- close(EXIT_FAILURE);
- }
- signal(SIGINT, handle);
- struct sockaddr_in serveAdd;
- //创建套接字
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- ERR_EXIT("socket"); //协议家族 服务类型(套接字类型), 协议弃用(0)
- }
- //确定服务端地址簇
- bzero(&serveAdd, sizeof(serveAdd)); //清0
- serveAdd.sin_family = AF_INET;
- serveAdd.sin_port = htons(SERVE_PORT);
- //将传入的ip字符串转换为 sin_addr
- if (inet_pton(AF_INET, argv[1], &serveAdd.sin_addr) == -1) {
- ERR_EXIT("inet_ntop");
- }
- //不需要绑定端口号 系统随机分配一个临时端口号, 直接连接
- if (connect(sockfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
- ERR_EXIT("connect");
- }
- while (1) { //死循环, 使用ctrl c信号关闭连接
- char buff[1024];
- printf("请说>>");
- scanf("%s", buff);
- write(sockfd, buff, strlen(buff));
- int n = read(sockfd, buff, sizeof(buff));
- if (n == -1) {
- ERR_EXIT("read");
- }
- buff[n] = 0;
- fprintf(stdout, ">>%s\n", buff);
- }
- }
- 本文首先通过 同步阻塞的原因 引出来 主线程被阻塞处理 网络 IO 服务了, 这样当他服务一个客户的时候, 其他客户都无法与服务器建立连接.
- 然后为了解决这个问题 提出来了 多进程 多线程模型 线程池模型 多路IO复用(遗留)
- 目的最终是为了解决 C10k 问题, 多进程 弊端分析(进程创建销毁, 切换) 系统资源耗费巨大, 最多支持 100 左右用户
- 为了减少系统资源消耗 + 减少切换压力, ---- 引出来 多线程模型, 多个线程共享所在进程中的进程资源, 线程间切换 仅仅只是线程私有数据和寄存器的切换(切换压力小)但是不停的创建销毁线程压力大
- 于是又引出来 线程池来实现线程的复用, 减去线程不停创建和销毁的压力
- C10k 问题就成为本章留疑了, 大家可以先自行讨论, 评论区给与简易,以及多路复用的含义解释呀等。。。。 总结时候如果存在不清晰读者可回溯
本文仅为自主学习复习使用, 借鉴了部分网络资源, 如果有不同意见,都可留言讨论,共同学习进步
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。