【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本
生活随笔
收集整理的這篇文章主要介紹了
【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
此為牛客Linux C++課程和黑馬Linux系統編程筆記。
1. 多進程版
1.1 思路
大體思路與上一篇的單進程版服務器–客戶端類似,都是遵循下圖:
多進程版本有以下幾點需要注意:
1.2 服務端
/*實現一個簡單的多進程服務器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h>// 設定一個服務器端口號 #define SERV_IP "127.0.0.1" #define SERV_PORT 7777void wait_child(int signo) {while(1) {waitpid(-1, NULL, WNOHANG); // 回收任意子進程并設置成非阻塞}return; }int main() {int lfd, cfd; // 用于監聽的文件描述符和用于與客戶端通信的文件描述符struct sockaddr_in serv_addr, clie_addr; // 服務器和客戶端的sockaddrlfd = socket(AF_INET, SOCK_STREAM, 0); // 服務端套接字serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT); // 注意轉化成網絡字節序inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 注意轉化成網絡字節序bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 與ip和端口號綁定listen(lfd, 128); // 指定最多同時連接數128int pid;while(1) {// 父進程循環進行acceptint clie_addr_len = sizeof(clie_addr);cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);char clie_IP[BUFSIZ];printf("Client IP: %s, client port: %d connected\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)),ntohs(clie_addr.sin_port));pid = fork();if(pid > 0) {// 父進程close(cfd); // 父進程只需要循環監聽,不需要與客戶端進行數據交互,故關閉signal(SIGCHLD, wait_child); // 回收子進程,避免產生僵尸進程,也可以用sigaction} else if(pid == 0) {// 子進程,注意這里的寫法,因為子進程不需要循環,所以把子進程的邏輯定義在循環外部,在這里break出去close(lfd); // 子進程不需要監聽,所以把子進程的lfd關掉break;} else {perror("fork error");exit(1);}}if(pid == 0) {// 子進程負責跟一個客戶端完成數據交互while(1) {char buf[BUFSIZ];int len;len = read(cfd, buf, sizeof(buf));if(len > 0) {// 小寫轉大寫int i;for(i = 0; i < len; ++i) {if(buf[i] >= 'a' && buf[i] <= 'z') {buf[i] -= 32;}}write(cfd, buf, len); // 寫回給客戶端write(STDOUT_FILENO, buf, len);} else if(len == 0){// ret為0說明讀完了,表示客戶端已關閉close(cfd);exit(1);} else {perror("read error");exit(1);}}}return 0; }為突出主體,未寫錯誤檢測與錯誤提示
1.3 客戶端
/*實現一個簡單的多進程服務器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h>// 服務器的ip和端口 #define SERV_IP "127.0.0.1" #define SERV_PORT 7777int main() {int ret; // 用于錯誤檢測int cfd; // 用于寫入數據傳輸給服務端的socket的文件描述符cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd == -1) {perror("socket error");exit(1);}// bind() 可以不調用bind(), linux會隱式地綁定struct sockaddr_in serv_addr; // 因為要連接服務端,這里的sockadd_in是用于指定服務端的ip和端口bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 調用ip轉換函數,把字符串ip轉化為網絡字節序ret = connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));if(ret == -1) {perror("connect error");exit(1);}// 從終端讀取內容while(1) {char buf[BUFSIZ];fgets(buf, sizeof(buf), stdin); // 讀一行// 寫入到cfd中,傳輸給服務端ret = write(cfd, buf, strlen(buf)); // 注意不要寫成sizeof(buf),sizeof是在內存中所占的大小,strlen是到第一個'\0'位止。if(ret == -1) {perror("write error");exit(1);}// read在讀socket時默認時阻塞的,阻塞等待服務端傳輸數據int len;len = read(cfd, buf, sizeof(buf));if(len == -1) {perror("read error");exit(1);}printf("%s", buf);}close(cfd);return 0; }為突出主體,未寫錯誤檢測與錯誤提示
2. 多線程版本
使用與多進程完全類似的思路,無非是用線程來實現。
2.1 服務端
/*實現一個簡單的多線程服務器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <pthread.h> #include <strings.h>// 設定一個服務器端口號 #define SERV_IP "127.0.0.1" #define SERV_PORT 8888struct s_info /* 該結構體用于給子線程函數傳參 */ {struct sockaddr_in clie_addr; // 客戶端ip和端口號int cfd; // 通信所用的文件描述符 };void* do_work(void* arg) {// 子線程負責小寫轉大寫struct s_info *ts = (struct s_info*)arg;while(1) {char buf[BUFSIZ];int len;len = read(ts->cfd, buf, sizeof(buf));if(len > 0) {// 小寫轉大寫int i;for(i = 0; i < len; ++i) {if(buf[i] >= 'a' && buf[i] <= 'z') {buf[i] -= 32;}}write(ts->cfd, buf, len); // 寫回給客戶端write(STDOUT_FILENO, buf, len);} else if(len == 0){// ret為0說明讀完了,表示客戶端已關閉close(ts->cfd);exit(1);} else {perror("read error");exit(1);}}return (void*)0; }int main() {int lfd, cfd; // 用于監聽的文件描述符和用于與客戶端通信的文件描述符struct sockaddr_in serv_addr, clie_addr; // 服務器和客戶端的sockaddrlfd = socket(AF_INET, SOCK_STREAM, 0); // 服務端套接字serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT); // 注意轉化成網絡字節序inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 注意轉化成網絡字節序bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 與ip和端口號綁定listen(lfd, 128); // 指定最多同時連接數128struct s_info ts;pthread_t tid;while(1) {// 主線程循環進行acceptint clie_addr_len = sizeof(clie_addr);cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);char clie_IP[BUFSIZ];printf("Client IP: %s, client port: %d connected\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)),ntohs(clie_addr.sin_port));bzero(&ts, sizeof(ts));ts.cfd = cfd;ts.clie_addr = clie_addr;pthread_create(&tid, NULL, do_work, (void*)&ts);pthread_detach(tid); // 子線程分離,防止產生僵尸線程}printf("what happened?");return 0; }2.2 客戶端
/*實現一個簡單的多線程服務器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h>// 服務器的ip和端口 #define SERV_IP "127.0.0.1" #define SERV_PORT 8888int main() {int ret; // 用于錯誤檢測int cfd; // 用于寫入數據傳輸給服務端的socket的文件描述符cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd == -1) {perror("socket error");exit(1);}// bind() 可以不調用bind(), linux會隱式地綁定struct sockaddr_in serv_addr; // 因為要連接服務端,這里的sockadd_in是用于指定服務端的ip和端口bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 調用ip轉換函數,把字符串ip轉化為網絡字節序ret = connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));if(ret == -1) {perror("connect error");exit(1);}// 從終端讀取內容while(1) {char buf[BUFSIZ];fgets(buf, sizeof(buf), stdin); // 讀一行// 寫入到cfd中,傳輸給服務端ret = write(cfd, buf, strlen(buf)); // 注意不要寫成sizeof(buf),sizeof是在內存中所占的大小,strlen是到第一個'\0'位止。if(ret == -1) {perror("write error");exit(1);}// read在讀socket時默認時阻塞的,阻塞等待服務端傳輸數據int len;len = read(cfd, buf, sizeof(buf));if(len == -1) {perror("read error");exit(1);}printf("%s", buf);}close(cfd);return 0; }總結
以上是生活随笔為你收集整理的【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成都欢乐谷泳裤多少钱
- 下一篇: 【Linux网络编程学习】I/O多路复用