帮人做网站如何收费,足球比赛直播回放,品牌营销理论,google海外版此为牛客Linux C课程和黑马Linux系统编程笔记。
1. 多进程版
1.1 思路
大体思路与上一篇的单进程版服务器–客户端类似#xff0c;都是遵循下图#xff1a; 多进程版本有以下几点需要注意#xff1a;
由于TCP是点对点连接#xff0c;服务器主进程连接了一个客户端以后…此为牛客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;
}