日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本

發(fā)布時(shí)間:2023/11/30 linux 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

此為??蚅inux C++課程和黑馬Linux系統(tǒng)編程筆記。

1. 多進(jìn)程版

1.1 思路

大體思路與上一篇的單進(jìn)程版服務(wù)器–客戶端類似,都是遵循下圖:

多進(jìn)程版本有以下幾點(diǎn)需要注意:

  • 由于TCP是點(diǎn)對點(diǎn)連接,服務(wù)器主進(jìn)程連接了一個(gè)客戶端以后就無法再與其他客戶端相連,所以多進(jìn)程版的服務(wù)器中的父進(jìn)程只負(fù)責(zé)監(jiān)聽,連接并信息傳輸?shù)墓ぷ鹘唤o子進(jìn)程完成。每當(dāng)accept到一個(gè)客戶端的連接請求,就fork出一個(gè)子進(jìn)程來處理。
  • 父進(jìn)程負(fù)責(zé)監(jiān)聽的同時(shí),也要回收子進(jìn)程的資源,避免產(chǎn)生僵尸進(jìn)程。
  • 1.2 服務(wù)端

    /*實(shí)現(xiàn)一個(gè)簡單的多進(jìn)程服務(wù)器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h>// 設(shè)定一個(gè)服務(wù)器端口號 #define SERV_IP "127.0.0.1" #define SERV_PORT 7777void wait_child(int signo) {while(1) {waitpid(-1, NULL, WNOHANG); // 回收任意子進(jìn)程并設(shè)置成非阻塞}return; }int main() {int lfd, cfd; // 用于監(jiān)聽的文件描述符和用于與客戶端通信的文件描述符struct sockaddr_in serv_addr, clie_addr; // 服務(wù)器和客戶端的sockaddrlfd = socket(AF_INET, SOCK_STREAM, 0); // 服務(wù)端套接字serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT); // 注意轉(zhuǎn)化成網(wǎng)絡(luò)字節(jié)序inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 注意轉(zhuǎn)化成網(wǎng)絡(luò)字節(jié)序bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 與ip和端口號綁定listen(lfd, 128); // 指定最多同時(shí)連接數(shù)128int pid;while(1) {// 父進(jìn)程循環(huán)進(jìn)行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) {// 父進(jìn)程close(cfd); // 父進(jìn)程只需要循環(huán)監(jiān)聽,不需要與客戶端進(jìn)行數(shù)據(jù)交互,故關(guān)閉signal(SIGCHLD, wait_child); // 回收子進(jìn)程,避免產(chǎn)生僵尸進(jìn)程,也可以用sigaction} else if(pid == 0) {// 子進(jìn)程,注意這里的寫法,因?yàn)樽舆M(jìn)程不需要循環(huán),所以把子進(jìn)程的邏輯定義在循環(huán)外部,在這里break出去close(lfd); // 子進(jìn)程不需要監(jiān)聽,所以把子進(jìn)程的lfd關(guān)掉break;} else {perror("fork error");exit(1);}}if(pid == 0) {// 子進(jìn)程負(fù)責(zé)跟一個(gè)客戶端完成數(shù)據(jù)交互while(1) {char buf[BUFSIZ];int len;len = read(cfd, buf, sizeof(buf));if(len > 0) {// 小寫轉(zhuǎn)大寫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說明讀完了,表示客戶端已關(guān)閉close(cfd);exit(1);} else {perror("read error");exit(1);}}}return 0; }

    為突出主體,未寫錯(cuò)誤檢測與錯(cuò)誤提示

    1.3 客戶端

    /*實(shí)現(xiàn)一個(gè)簡單的多進(jìn)程服務(wù)器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h>// 服務(wù)器的ip和端口 #define SERV_IP "127.0.0.1" #define SERV_PORT 7777int main() {int ret; // 用于錯(cuò)誤檢測int cfd; // 用于寫入數(shù)據(jù)傳輸給服務(wù)端的socket的文件描述符cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd == -1) {perror("socket error");exit(1);}// bind() 可以不調(diào)用bind(), linux會隱式地綁定struct sockaddr_in serv_addr; // 因?yàn)橐B接服務(wù)端,這里的sockadd_in是用于指定服務(wù)端的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); // 調(diào)用ip轉(zhuǎn)換函數(shù),把字符串ip轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序ret = connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));if(ret == -1) {perror("connect error");exit(1);}// 從終端讀取內(nèi)容while(1) {char buf[BUFSIZ];fgets(buf, sizeof(buf), stdin); // 讀一行// 寫入到cfd中,傳輸給服務(wù)端ret = write(cfd, buf, strlen(buf)); // 注意不要寫成sizeof(buf),sizeof是在內(nèi)存中所占的大小,strlen是到第一個(gè)'\0'位止。if(ret == -1) {perror("write error");exit(1);}// read在讀socket時(shí)默認(rèn)時(shí)阻塞的,阻塞等待服務(wù)端傳輸數(shù)據(jù)int len;len = read(cfd, buf, sizeof(buf));if(len == -1) {perror("read error");exit(1);}printf("%s", buf);}close(cfd);return 0; }

    為突出主體,未寫錯(cuò)誤檢測與錯(cuò)誤提示

    2. 多線程版本

    使用與多進(jìn)程完全類似的思路,無非是用線程來實(shí)現(xiàn)。

    2.1 服務(wù)端

    /*實(shí)現(xiàn)一個(gè)簡單的多線程服務(wù)器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <pthread.h> #include <strings.h>// 設(shè)定一個(gè)服務(wù)器端口號 #define SERV_IP "127.0.0.1" #define SERV_PORT 8888struct s_info /* 該結(jié)構(gòu)體用于給子線程函數(shù)傳參 */ {struct sockaddr_in clie_addr; // 客戶端ip和端口號int cfd; // 通信所用的文件描述符 };void* do_work(void* arg) {// 子線程負(fù)責(zé)小寫轉(zhuǎn)大寫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) {// 小寫轉(zhuǎn)大寫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說明讀完了,表示客戶端已關(guān)閉close(ts->cfd);exit(1);} else {perror("read error");exit(1);}}return (void*)0; }int main() {int lfd, cfd; // 用于監(jiān)聽的文件描述符和用于與客戶端通信的文件描述符struct sockaddr_in serv_addr, clie_addr; // 服務(wù)器和客戶端的sockaddrlfd = socket(AF_INET, SOCK_STREAM, 0); // 服務(wù)端套接字serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT); // 注意轉(zhuǎn)化成網(wǎng)絡(luò)字節(jié)序inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 注意轉(zhuǎn)化成網(wǎng)絡(luò)字節(jié)序bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 與ip和端口號綁定listen(lfd, 128); // 指定最多同時(shí)連接數(shù)128struct s_info ts;pthread_t tid;while(1) {// 主線程循環(huán)進(jìn)行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); // 子線程分離,防止產(chǎn)生僵尸線程}printf("what happened?");return 0; }

    2.2 客戶端

    /*實(shí)現(xiàn)一個(gè)簡單的多線程服務(wù)器-客戶端通信*/#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h>// 服務(wù)器的ip和端口 #define SERV_IP "127.0.0.1" #define SERV_PORT 8888int main() {int ret; // 用于錯(cuò)誤檢測int cfd; // 用于寫入數(shù)據(jù)傳輸給服務(wù)端的socket的文件描述符cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd == -1) {perror("socket error");exit(1);}// bind() 可以不調(diào)用bind(), linux會隱式地綁定struct sockaddr_in serv_addr; // 因?yàn)橐B接服務(wù)端,這里的sockadd_in是用于指定服務(wù)端的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); // 調(diào)用ip轉(zhuǎn)換函數(shù),把字符串ip轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序ret = connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));if(ret == -1) {perror("connect error");exit(1);}// 從終端讀取內(nèi)容while(1) {char buf[BUFSIZ];fgets(buf, sizeof(buf), stdin); // 讀一行// 寫入到cfd中,傳輸給服務(wù)端ret = write(cfd, buf, strlen(buf)); // 注意不要寫成sizeof(buf),sizeof是在內(nèi)存中所占的大小,strlen是到第一個(gè)'\0'位止。if(ret == -1) {perror("write error");exit(1);}// read在讀socket時(shí)默認(rèn)時(shí)阻塞的,阻塞等待服務(wù)端傳輸數(shù)據(jù)int len;len = read(cfd, buf, sizeof(buf));if(len == -1) {perror("read error");exit(1);}printf("%s", buf);}close(cfd);return 0; }

    總結(jié)

    以上是生活随笔為你收集整理的【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。