【Linux网络编程部分----多进程高并发poll模型】
目錄
前言
背景
分析
編寫步驟
服務(wù)器:
客戶端:
?服務(wù)器端代碼
附:文件操作部分
附:目錄操作部分
客戶端代碼
全部代碼
頭文件部分
服務(wù)器全部代碼
客戶端所有代碼
總結(jié):
?
前言
????????本文采用? Visual Studio 2022 運(yùn)用遠(yuǎn)程登陸,來對(duì)虛擬機(jī)內(nèi)的? Linux 進(jìn)行操作
? ? ? ? 如果不知道怎么使用的話可參考:?linux遠(yuǎn)程開發(fā)——使用vs2019遠(yuǎn)程連接linux_vs2019遠(yuǎn)程調(diào)試linux_似末的博客-CSDN博客https://blog.csdn.net/wmcy123/article/details/123415371?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167741273216782427438492%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167741273216782427438492&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-3-123415371-null-null.142^v73^insert_down1,201^v4^add_ask,239^v2^insert_chatgpt&utm_term=vs%E9%93%BE%E6%8E%A5linux&spm=1018.2226.3001.4187
背景
? ? ? ? 在TCP協(xié)議下,使用套接字(socket)多進(jìn)程(fork)之間進(jìn)行通信,并且客戶端能夠從服務(wù)器設(shè)定下的地址上獲取文件。
分析
? ? ? ? 要分別從服務(wù)器和客戶端兩個(gè)方面入手,采取網(wǎng)絡(luò)編程所固定的格式來進(jìn)行編寫
? ? ? ? 服務(wù)器:要不斷的監(jiān)聽是否有客戶端來進(jìn)行通信,并且采用多進(jìn)程的方式使得客戶端之間互不影響
? ? ? ? 客戶端:要可隨時(shí)加入服務(wù)器,并且能夠自主下載服務(wù)端下面的指定文件
編寫步驟
服務(wù)器:
????????第一步:創(chuàng)建套接字
????????第二步:監(jiān)聽套接字
????????第三步:等待客戶端連接
????????第四步:發(fā)送數(shù)據(jù)給客戶端
? ? ? ? 第五步:關(guān)閉套接字
客戶端:
????????第一步:創(chuàng)建套接字
????????第二步:鏈接服務(wù)器
????????第三步:讀寫服務(wù)器
????????第四步:關(guān)閉套接字
?服務(wù)器端代碼
? ? ? ? 一、我們先做前期準(zhǔn)備,先創(chuàng)建套接字(socket),并先確定其中的內(nèi)容
int tcp_sock = socket(AF_INET, SOCK_STREAM, 0); //創(chuàng)建套接字struct sockaddr_in Ipv4 {}; //定義一個(gè)Ipv4的結(jié)構(gòu)體信息Ipv4.sin_family = AF_INET; //協(xié)議為IPV4Ipv4.sin_addr.s_addr = 0; //Ip地址Ipv4.sin_port = htons(PORT); //端口號(hào) 此處PORT為宏定義? ? ? ? 二、創(chuàng)建完后,在對(duì)其進(jìn)行綁定,并且開始監(jiān)聽
if (bind(tcp_sock, (struct sockaddr*)&Ipv4, sizeof(Ipv4)) < 0) //綁定{perror("bind");return 1;}listen(tcp_sock, 10); //監(jiān)聽將主動(dòng)套接字變?yōu)楸粍?dòng)套接字 監(jiān)聽10路? ? ? ?三、創(chuàng)建? pollfd? 類型的結(jié)構(gòu)來確保我們使用 poll 函數(shù)
struct pollfd fds[SIZE]{}; // 將服務(wù)器加入到輪詢表中 fds[index].fd = tcp_sock; // 有數(shù)據(jù)可讀事件 */? ? ? ? 四、準(zhǔn)備工作已經(jīng)做完了,就開始服務(wù)器的操作
while (true){if (poll(fds, SIZE, 3000) == 0){//printf("time out \n"); //檢測(cè)continue;}for (int i = 0; i < index + 1; i++){if (tcp_sock == fds[i].fd) //主服務(wù)器 {if (fds[i].revents & POLLIN){int cid = accept(tcp_sock, NULL, NULL);fds[index].fd = cid; //將服務(wù)器加入到輪詢表中fds[index].events = POLLIN; //有數(shù)據(jù)可讀事件index++;printf("someone coming\n");}}else //與客戶端交互{if (fork() == 0) //多進(jìn)程 子進(jìn)程進(jìn)行操作。避免出現(xiàn)孤兒進(jìn)程{if (fds[i].revents & POLLIN){memset(buf, 0, SIZE); //將buf的內(nèi)容全部置為0buf[SIZE - 1] = 1;len = read(fds[i].fd, buf, SIZE); //讀取數(shù)據(jù)if (len > 0){if (strcmp(buf, "file") == 0){printf("sent file to %d\n", i);dir(fds[i].fd); // 目錄文件操作read(fds[i].fd, buf, SIZE);file(fds[i].fd, buf); memset(buf, 0, SIZE);}if (buf[SIZE - 1] != 0){printf("%d --- :%s\n", i, buf); }write(fds[i].fd, buf, len);}}exit(-1);} }waitpid(-1, NULL, WNOHANG);}}附:文件操作部分
void file(int tcp_socket,char name[20]) { data_t cmd{};/**** 獲取文件名 + 文件大小 *****/struct stat stat_buf {};if (stat(name, &stat_buf) != 0) //獲取文件信息{perror("stat");return;}cmd.file_len = stat_buf.st_size;char* str = strrchr(name, '/');if (str != NULL) //找到了 {str += 1; //往后移動(dòng)一位}else{str = name;}strcpy(cmd.file_name, str); //獲取文件名printf("file name:%s\n", cmd.file_name);printf("file size:%d\n", cmd.file_len);int fd = open(name, O_RDONLY);if (fd < 0){perror("open");return;}write(tcp_socket, &cmd, sizeof(cmd)); //發(fā)送包頭:文件名 + 文件大小/***** 循環(huán)讀取文件并發(fā)送 ****/char buf[1024]{};int len = 0;while (cmd.file_len > 0){len = read(fd, buf, 1024); //讀文件write(tcp_socket, buf, len); //寫入cmd.file_len -= len;}/****4.關(guān)閉套接字 *****/close(fd); }附:目錄操作部分
void dir(int cid) {DIR* dir = opendir("/home/student/projects/ProjectDemo/bin/x64/Debug"); // 打開一個(gè)目錄char buf[SIZE]{};if (dir == NULL){perror("opendir");return ;}struct dirent* dnt;while ((dnt = readdir(dir)) != NULL) // 只要返回結(jié)果不為NULL,就一直遍歷{printf("%s\n", dnt->d_name);strcpy(buf, dnt->d_name);write(cid, buf, sizeof(buf));}strcpy(buf, "quitquit");write(cid, buf, sizeof(buf));closedir(dir); }客戶端代碼
? ? ? ? 客戶端代碼與服務(wù)器端代碼大同小異,可直接參考服務(wù)器端來編寫
int cid = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in IPV4 {};IPV4.sin_family = AF_INET;IPV4.sin_addr.s_addr = 0; IPV4.sin_port = htons(PORT); char name[20]{};if (connect(cid, (struct sockaddr*)&IPV4, sizeof(IPV4)) < 0){perror("conncet");return 1;}char buf[128]{};while (true){printf("sent message to service:\n");gets(buf);write(cid, buf, sizeof(buf));if (strncmp(buf, "file",4) == 0){dir(cid);printf("\n");printf("receive a file is \n");scanf("%s", name);write(cid, name, sizeof(buf));file(cid,name); //文件操作} if (strncmp(buf, "quit", 4) == 0){break;}}全部代碼
? ? ? ? 在此,給出全部的代碼以供大家學(xué)習(xí)
頭文件部分
#include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/ip.h> #include <netinet/in.h> #include <string.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <poll.h> #include <sys/wait.h> #include <dirent.h>服務(wù)器全部代碼
typedef struct {char file_name[20]; //文件名int file_len; //文件大小 } data_t;constexpr auto PORT = 10000; constexpr auto IP = "192.168.248.128"; constexpr auto SIZE = 1024;void dir(int cid) {DIR* dir = opendir("/home/student/projects/ProjectDemo/bin/x64/Debug"); // 打開一個(gè)目錄char buf[SIZE]{};if (dir == NULL){perror("opendir");return ;}struct dirent* dnt;while ((dnt = readdir(dir)) != NULL) // 只要返回結(jié)果不為NULL,就一直遍歷{printf("%s\n", dnt->d_name);strcpy(buf, dnt->d_name);write(cid, buf, sizeof(buf));}strcpy(buf, "quitquit");write(cid, buf, sizeof(buf));closedir(dir); }void file(int tcp_socket,char name[20]) { data_t cmd{};/**** 獲取文件名 + 文件大小 *****/struct stat stat_buf {};if (stat(name, &stat_buf) != 0) //獲取文件信息{perror("stat");return;}cmd.file_len = stat_buf.st_size;char* str = strrchr(name, '/');if (str != NULL) //找到了 {str += 1; //往后移動(dòng)一位}else{str = name;}strcpy(cmd.file_name, str); //獲取文件名printf("file name:%s\n", cmd.file_name);printf("file size:%d\n", cmd.file_len);int fd = open(name, O_RDONLY);if (fd < 0){perror("open");return;}write(tcp_socket, &cmd, sizeof(cmd)); //發(fā)送包頭:文件名 + 文件大小/***** 循環(huán)讀取文件并發(fā)送 ****/char buf[1024]{};int len = 0;while (cmd.file_len > 0){len = read(fd, buf, 1024); //讀文件write(tcp_socket, buf, len); //寫入cmd.file_len -= len;}/****4.關(guān)閉套接字 *****/close(fd); }int main() //多路復(fù)用高并發(fā) {int tcp_sock = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in Ipv4 {};Ipv4.sin_family = AF_INET;Ipv4.sin_addr.s_addr = 0;Ipv4.sin_port = htons(PORT);if (bind(tcp_sock, (struct sockaddr*)&Ipv4, sizeof(Ipv4)) < 0){perror("bind");return 1;}listen(tcp_sock, 10);int index = 0;struct pollfd fds[SIZE]{};/* 將服務(wù)器加入到輪詢表中 */fds[index].fd = tcp_sock;/* 有數(shù)據(jù)可讀事件 */fds[index].events = POLLIN;index++;char buf[SIZE]{};int len = 0;while (true){if (poll(fds, SIZE, 3000) == 0){//printf("time out \n");continue;}for (int i = 0; i < index + 1; i++){if (tcp_sock == fds[i].fd)// 主服務(wù)器 {if (fds[i].revents & POLLIN){int cid = accept(tcp_sock, NULL, NULL);fds[index].fd = cid; //將服務(wù)器加入到輪詢表中fds[index].events = POLLIN; //有數(shù)據(jù)可讀事件index++;printf("someone coming\n");}}else //與客戶端交互{if (fork() == 0){if (fds[i].revents & POLLIN){memset(buf, 0, SIZE);buf[SIZE - 1] = 1;len = read(fds[i].fd, buf, SIZE);if (len > 0){if (strcmp(buf, "file") == 0){printf("sent file to %d\n", i);dir(fds[i].fd);read(fds[i].fd, buf, SIZE);file(fds[i].fd, buf);memset(buf, 0, SIZE);}if (buf[SIZE - 1] != 0){printf("%d --- :%s\n", i, buf);}write(fds[i].fd, buf, len);}}exit(-1);} }waitpid(-1, NULL, WNOHANG);}}close(tcp_sock);return 1; }客戶端所有代碼
constexpr auto PORT = 10000; constexpr auto IP = "192.168.248.128"; constexpr auto SIZE = 1024;typedef struct {char file_name[20]; //文件名int file_len; //文件大小 } data_t;void dir(int cid) {char buf[SIZE]{};printf("you can get:\n");read(cid, buf, sizeof(buf));int i = 0;while (strcmp(buf,"quitquit") != 0){ printf("%s ", buf);read(cid, buf, sizeof(buf));i++;if (i == 5){printf("\n");i = 0;}}}void file(int cid,char name[20]) {data_t cmd;read(cid, &cmd, sizeof(cmd)); //接收文件名 + 大小remove(name);int fd = open(name, O_CREAT, 0666);if (fd < 0){perror("open");return;}char* buf = (char*)malloc(1024);int len = 0;while (cmd.file_len > 0){len = read(cid, buf, 1024);if (len <= 0){perror("read");break;}cmd.file_len -= len;write(fd, buf, len);}printf("over\n"); } int main()//客戶端 {int cid = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in IPV4 {};IPV4.sin_family = AF_INET;IPV4.sin_addr.s_addr = 0; //ip地址IPV4.sin_port = htons(PORT); //端口號(hào)char name[20]{};if (connect(cid, (struct sockaddr*)&IPV4, sizeof(IPV4)) < 0){perror("conncet");return 1;}char buf[128]{};while (true){printf("sent message to service:\n");gets(buf);write(cid, buf, sizeof(buf));if (strncmp(buf, "file",4) == 0){dir(cid);printf("\n");printf("receive a file is \n");scanf("%s", name);write(cid, name, sizeof(buf));file(cid,name);} if (strncmp(buf, "quit", 4) == 0){break;}}close(cid);return 1; }總結(jié):
? ? ? ? 網(wǎng)絡(luò)編程以固定的套路走,只要熟練了,按照一定的步驟走,網(wǎng)絡(luò)編程就有跡可循。搞懂并不是一個(gè)特別困難的過程。加油!
總結(jié)
以上是生活随笔為你收集整理的【Linux网络编程部分----多进程高并发poll模型】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hihoCoder 1166 交换代数
- 下一篇: 03计算圆的面积