600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本

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

时间:2021-06-05 14:12:11

相关推荐

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

此为牛客Linux C++课程和黑马Linux系统编程笔记。

1. 多进程版

1.1 思路

大体思路与上一篇的单进程版服务器–客户端类似,都是遵循下图:

多进程版本有以下几点需要注意:

由于TCP是点对点连接,服务器主进程连接了一个客户端以后就无法再与其他客户端相连,所以多进程版的服务器中的父进程只负责监听,连接并信息传输的工作交给子进程完成。每当accept到一个客户端的连接请求,就fork出一个子进程来处理。父进程负责监听的同时,也要回收子进程的资源,避免产生僵尸进程。

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;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。