网络编程 TCP编程 Linux环境 C语言实现

所有基于数据传输通信的程序,都会被分成两种角色:

1. 服务端:又称为服务器 server 提供一种通信服务的进程

基本工作过程是:1> 接收请求数据 2> 处理请求数据 3> 发送处理结果

2. 客户端:client 使用一种通信服务的进程

基本工作过程是:1> 组织请求数据 2> 发送请求数据 3>接收请求回应(即服务端的处理结果) 4>向用户展示处理结果

TCP编程就是学习如何利用传输层TCP协议规定的传输方式来传输应用层PDU


8.1 基本代码框架

​客户端代码套路

int sockfd = -1;
struct sockaddr_in servaddr;
int ret = 0;1. 创建一个使用TCP进行传输的引擎对象,并获得该引擎对象的描述符 --- socketsockfd = socket(AF_INET,SOCK_STREAM,0);2. 填写服务端的IP地址和端口号bzero(&servaddr,sizeof(servaddr)); //memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(服务端的端口号);inet_aton("服务端的点分十进制字符串形式的IP地址",&servaddr.sin_addr);
3. 与服务端建立连接 ------ connectret = connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));if(ret){printf("connect server failed\n");......}4. 与服务端进行数据交互(数据传输)  ------ read/write  send/recv5. 无需继续与服务端进行数据传输时,应及时调用closeclose(sockfd);sockfd = -1;

服务端代码套路

int connectfd = -1;
int datafd = -1;
struct sockaddr_in servaddr;
int ret = 0;1. 创建一个使用TCP进行传输的引擎对象,并获得该引擎对象的描述符 --- socketconnectfd = socket(AF_INET,SOCK_STREAM,0);2. 填写服务端自己的IP地址和端口号bzero(&servaddr,sizeof(servaddr)); //memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(服务端的端口号);inet_aton("服务端的点分十进制字符串形式的IP地址",&servaddr.sin_addr);3. 为引擎对象绑定服务端自己的IP地址和端口号 ------ bindret = bind(connectfd,(struct sockaddr *)&servaddr,sizeof(servaddr));4. 将第1步的引擎对象变为管理监控连接用的引擎对象 ------listenret += listen(connectfd,9);if(ret){printf("bind or listen failed\n");.......}5. 循环检查有没有客户端与本服务端建立好连接  ------accept一旦发现有客户端与本服务端建立好连接就创建一个与该客户端进行数据传输用的引擎对象,并获得该引擎对象的描述符利用数据传输用的引擎对象与对应客户端进行数据交互  ----- read/write send/recv无需继续与与对应客户端进行数据交互时,应及时关闭数据传输用的引擎对象 ----closewhile(1){datafd = accept(connectfd,NULL,NULL);if(datafd < 0){if(errno == EINTR)    continue;else{printf("accept error");break;}}调用read/write 或 send/recv与对应客户端进行数据交互close(datafd);datafd = -1;}6. 无需继续监控连接时,应及时调用close关闭由第4步修改后的引擎对象 ---- closeclose(connectfd);connectfd = -1;

原本按网络通信的特点,需要客户端进程和服务端进程运行在处于同一网络的两台不同的主机上,但为了减轻开发网络程序的成本,几乎所有支持网络的操作系统都设计了一个虚拟网卡,称为本地环回(loopback),所有发给该网卡的数据不向外传输,只能由本机的其它进程接收,这样就可以在通过一台电脑主机上同时运行客户端进程和服务端进程,从而减轻了开发成本。因特网还给本地环回网卡指定了专门的IP地址:127.0.0.1。网络程序实际运行客户端、服务端还是运行不同主机上的。


socket:套接字

在欧美国家,以下三种东西的统称叫socket:

  1. 插头---------主动socket -------- TCP客户端socket函数返回的描述符
  2. 插座或插排 ---被动socket --------- TCP服务端经过listen函数处理后的描述符
  3. 插排上的插孔 ---插排用来给某个具有插头的用电设备提供电压服务 ------ TCP服务端accept函数每次返回的描述符


8.2 相关函数说明

客户端与服务端的公共函数:

​客户端专用函数:

服务端专用函数:

8.3 框架代码封装

客户端:socket + 填服务端socket地址 + connect 封装成一个函数

int create_tcp_client_socket(const char *serverip,unsigned short port)

服务端:socket + 填服务端socket地址 + bind + listen封装成一个函数

int create_tcp_server_socket(const char *serverip,unsigned short port,int backlog)

8.4 服务端并发

三种基本并发方案

1. 多进程并发

每次 accept 正常返回就创建一个子进程,由这个子进程专门负责与对应客户端进行数据交互,而父进程继续下一轮调用 accept

避免子进程为僵尸进程的方法:

1> 委托给祖先进程善后 ------- 代码较为复杂

2> 父进程一开始就调用:signal(SIGCHLD,SIG_IGN); ----- 简单,推荐采用

2. 多线程并发

每次 accept 正常返回就创建一个新线程,由这个新线程专门负责与对应客户端进行数据交互,而主线程继续下一轮调用 accept

避免新线程为僵尸线程的方法:让新线程成为分离的线程

1> 用线程属性在创建新线程时将其做成分离的线程 ----- 代码较为复杂

2> 线程入口函数开头处调用:pthread_detach(pthread_self()); ---- 简单,推荐采用

3. 多路复用-----能达到并发相似的效果,但不是真正意义的并发


8.4.1 多路复用

多路复用机制只负责监控描述符对应对象是否有数据可读 或 可写 或 异常,数据的接收、发送、处理一概不管


8.4.2 select


select 函数两种形式的使用:

1. 粗放式使用------第1个参数传 FD_SETSIZE(宏体为1024的常量宏)

2. 精细式使用------第1个参数传所有参与监控的描述符的最大值 + 1

int find_max_fd(fd_set *pfds){int i = 0;for(i = FD_SETSIZE - 1;i >= 0;i--){if(FD_ISSET(i,pfds))    break;}return i;
}

8.4.3 poll

​需要设计一个元素类型是 struct pollfd 类型的顺序表来管理所有参与监控的描述符和它们的被监控事件

struct pollfd_seqlist{struct pollfd *p_pollfd;int cnt;int max;
};struct pollfd_seqlist *create_pollfd_seqlist(int max);
int destroy_pollfd_seqlist(struct pollfd_seqlist *psl);
int insert_fd_into_pollfd_seqlist(struct pollfd_seqlist *psl,int fd,short evt);
int remove_fd_from_pollfd_seqlist(struct pollfd_seqlist *psl,int fd);
int clear_all_revents(struct pollfd_seqlist *psl);

8.4.4 epoll

​​


8.4.5 三种服务端基本并发方案的比较:

1. 多进程: 占用资源太多 只要系统资源允许,同时并发数可以任意

2. 多线程:占用资源较少,但同时并发数受系统描述符数组大小的控制

3. 多路复用epoll:占用资源最少,但仅用于对任意客户端请求的处理都是短平快的场合,且同时并发数受系统描述符数组大小的控制


九、Socket属性

​发送、接收的超时设置

TCP长连接保活机制---心跳机制


改造代码:IPC 进程间通信 例题

示例代码:(只对server 服务端进行改造)

1.普通socket(不能并发)

#include "fileop_protocol.h"#include <errno.h>int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog);
int server_main_loop(int fd);
int main(int argc,char *argv[]){int servfd = -1;int intport = -1;unsigned short port = 0;if(argc < 3){printf("The argument is too few\n");return 1;}sscanf(argv[2],"%d",&intport);if(intport < 0 || intport > 0xFFFF){printf("The port:%d is invalid\n",intport);return 2;}port = intport;servfd = create_tcp_server_socket(argv[1],port,9);server_main_loop(servfd);close(servfd);servfd = -1;return 0;
}int handle_client(int fd);
int server_main_loop(int fd){int datafd = -1;while(1){datafd = accept(fd,NULL,NULL);if(datafd < 0){if(errno == EINTR)      continue;else{printf("Accept error\n");break;}}handle_client(datafd);}return 0;
}int handle_client(int fd){struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;while(1){preq = recv_fileop_pdu(fd);if(preq == NULL)        break;switch(preq->type){case FILE_LEN_REQ:prsp = create_file_len_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;case FILE_TYPE_REQ:prsp = create_file_type_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;default:break;}destroy_fileop_pdu(preq);preq = NULL;}//end while(1)close(fd);fd = -1;return 0;
}int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog){int servfd = -1;struct sockaddr_in servaddr;int ret = 0;servfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(svrport);inet_aton(psvrip,&servaddr.sin_addr);ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));ret += listen(servfd,backlog);if(ret){printf("bind or listen failed\n");close(servfd);servfd = -1;return -1;}return servfd;
}

2.多进程

#include "fileop_protocol.h"
#include <signal.h>
#include <errno.h>int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog);
int server_main_loop(int fd);int main(int argc,char *argv[]){int servfd = -1; int intport = -1; unsigned short port = 0;if(argc < 3){ printf("The argument is too few\n");return 1;}   sscanf(argv[2],"%d",&intport);if(intport < 0 || intport > 0xFFFF){printf("The port:%d is invalid\n",intport);return 2;}   port = intport;servfd = create_tcp_server_socket(argv[1],port,9);server_main_loop(servfd);close(servfd);servfd = -1; return 0;
}int handle_client(int fd);
int server_main_loop(int fd){int datafd = -1; pid_t pid;signal(SIGCHLD,SIG_IGN); // auto kill processwhile(1){datafd = accept(fd,NULL,NULL); // acceptif(datafd < 0){ if(errno == EINTR)  continue; // because waitelse{printf("Accept error\n");break;}}pid = fork(); // fork()if(pid < 0){ close(datafd);datafd = -1; }if(pid == 0){ // sonclose(fd);fd = -1; handle_client(datafd);exit(0);}else{ // parentsclose(datafd);datafd = -1; }}   return 0;
}int handle_client(int fd){struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;while(1){preq = recv_fileop_pdu(fd);if(preq == NULL)    break;switch(preq->type){case FILE_LEN_REQ:prsp = create_file_len_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;case FILE_TYPE_REQ:prsp = create_file_type_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;default:break;}destroy_fileop_pdu(preq);preq = NULL;}//end while(1)close(fd);fd = -1; return 0;
}int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog){int servfd = -1; struct sockaddr_in servaddr;int ret = 0;servfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(svrport);inet_aton(psvrip,&servaddr.sin_addr);ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));ret += listen(servfd,backlog);if(ret){printf("bind or listen failed\n");close(servfd);servfd = -1; return -1; }   return servfd;
}

3. 多线程

#include "fileop_protocol.h"
#include <signal.h>
#include <errno.h>
#include <pthread.h>int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog);
int server_main_loop(int fd);int main(int argc,char *argv[]){int servfd = -1; int intport = -1; unsigned short port = 0;if(argc < 3){ printf("The argument is too few\n");return 1;}   sscanf(argv[2],"%d",&intport);if(intport < 0 || intport > 0xFFFF){printf("The port:%d is invalid\n",intport);return 2;}   port = intport;servfd = create_tcp_server_socket(argv[1],port,9);server_main_loop(servfd);close(servfd);servfd = -1; return 0;
}void * handle_client(void *parg);
int server_main_loop(int fd){int datafd = -1; int ret = 0;pthread_t tid;while(1){datafd = accept(fd,NULL,NULL);if(datafd < 0){ if(errno == EINTR)  continue;else{printf("Accept error\n");break;}}ret = pthread_create(&tid,NULL,handle_client,(void *)(long)datafd);if(ret){close(datafd);datafd = -1; printf("pthead_create failed\n");}}   return 0;
}void * handle_client(void *parg){int fd = (int)(long)parg;struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;pthread_detach(pthread_self()); // thread detachwhile(1){preq = recv_fileop_pdu(fd); // recv pduif(preq == NULL)    break;switch(preq->type){case FILE_LEN_REQ:prsp = create_file_len_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;case FILE_TYPE_REQ:prsp = create_file_type_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;default:break;}destroy_fileop_pdu(preq);preq = NULL;}//end while(1)close(fd);fd = -1; return 0;
}int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog){int servfd = -1; struct sockaddr_in servaddr;int ret = 0;servfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(svrport);inet_aton(psvrip,&servaddr.sin_addr);ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));ret += listen(servfd,backlog);if(ret){printf("bind or listen failed\n");close(servfd);servfd = -1; return -1; }   return servfd;
}

4.粗放式select

#include "fileop_protocol.h"
#include <signal.h>
#include <errno.h>int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog);
int server_main_loop(int fd);int main(int argc,char *argv[]){int servfd = -1;int intport = -1;unsigned short port = 0;if(argc < 3){printf("The argument is too few\n");return 1;}sscanf(argv[2],"%d",&intport);if(intport < 0 || intport > 0xFFFF){printf("The port:%d is invalid\n",intport);return 2;}port = intport;servfd = create_tcp_server_socket(argv[1],port,9);server_main_loop(servfd);close(servfd);servfd = -1;return 0;
}int handle_client(int fd);
int server_main_loop(int fd){int datafd = -1;fd_set rfds;//作为select函数的第二参数fd_set bakrfds;//始终参与读监控的描述符,不保存监控结果int ret = 0;int i = 0;FD_ZERO(&bakrfds); // 初始化 FD_SET(fd,&bakrfds); // SETwhile(1){memcpy(&rfds,&bakrfds,sizeof(fd_set)); // readret = select(FD_SETSIZE,&rfds,NULL,NULL,NULL); //selectif(ret < 0){if(errno == EINTR)  continue;else{printf("select error\n");break;}}for(i = 0;i < FD_SETSIZE;i++){if(FD_ISSET(i,&rfds)){if(i == fd){//服务端管理连接用的描述符有数据可读(即有客户端与服务端建立了好逻辑连接)datafd = accept(fd,NULL,NULL);FD_SET(datafd,&bakrfds);}else{//值为i的描述符是与客户端进行数据交互用的描述符(即accept返回的描述符),其中有数据可读ret = handle_client(i);if(ret < 0){FD_CLR(i,&bakrfds);}}}}}return 0;
}int handle_client(int fd){struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;int flag = 0;do{preq = recv_fileop_pdu(fd);if(preq == NULL){flag = 1;break;}switch(preq->type){case FILE_LEN_REQ:prsp = create_file_len_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;case FILE_TYPE_REQ:prsp = create_file_type_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;default:break;}destroy_fileop_pdu(preq);preq = NULL;}while(0);if(flag){close(fd);fd = -1;return -1;}return 0;
}int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog){int servfd = -1;struct sockaddr_in servaddr;int ret = 0;servfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(svrport);inet_aton(psvrip,&servaddr.sin_addr);ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));ret += listen(servfd,backlog);if(ret){printf("bind or listen failed\n");close(servfd);servfd = -1;return -1;}return servfd;
}

5. 精细式select

#include "fileop_protocol.h"
#include <signal.h>
#include <errno.h>int get_max_fd(fd_set *pfds);
int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog);
int server_main_loop(int fd);int main(int argc,char *argv[]){int servfd = -1;int intport = -1;unsigned short port = 0;if(argc < 3){printf("The argument is too few\n");return 1;}sscanf(argv[2],"%d",&intport);if(intport < 0 || intport > 0xFFFF){printf("The port:%d is invalid\n",intport);return 2;}port = intport;servfd = create_tcp_server_socket(argv[1],port,9);server_main_loop(servfd);close(servfd);servfd = -1;return 0;
}int get_max_fd(fd_set *pfds){int i = 0;for(i = FD_SETSIZE - 1;i >= 0;i--)if(FD_ISSET(i,pfds))    break;return i;
}int handle_client(int fd);
int server_main_loop(int fd){int datafd = -1;fd_set rfds;//作为select函数的第二参数fd_set bakrfds;//始终参与读监控的描述符,不保存监控结果int ret = 0;int i = 0;int maxfd = -1;FD_ZERO(&bakrfds); // 初始化 FD_SET(fd,&bakrfds); // SETwhile(1){memcpy(&rfds,&bakrfds,sizeof(fd_set)); // readmaxfd = get_max_fd(&rfds);ret = select(maxfd + 1,&rfds,NULL,NULL,NULL); //selectif(ret < 0){if(errno == EINTR)  continue;else{printf("select error\n");break;}}for(i = 0;i < maxfd + 1;i++){if(FD_ISSET(i,&rfds)){if(i == fd){//服务端管理连接用的描述符有数据可读(即有客户端与服务端建立了好逻辑连接)datafd = accept(fd,NULL,NULL);FD_SET(datafd,&bakrfds);}else{//值为i的描述符是与客户端进行数据交互用的描述符(即accept返回的描述符),其中有数据可读ret = handle_client(i);if(ret < 0){FD_CLR(i,&bakrfds);}}}}}return 0;
}int handle_client(int fd){struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;int flag = 0;do{preq = recv_fileop_pdu(fd);if(preq == NULL){flag = 1;break;}switch(preq->type){case FILE_LEN_REQ:prsp = create_file_len_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;case FILE_TYPE_REQ:prsp = create_file_type_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;default:break;}destroy_fileop_pdu(preq);preq = NULL;}while(0);if(flag){close(fd);fd = -1;return -1;}return 0;
}int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog){int servfd = -1;struct sockaddr_in servaddr;int ret = 0;servfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(svrport);inet_aton(psvrip,&servaddr.sin_addr);ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));ret += listen(servfd,backlog);if(ret){printf("bind or listen failed\n");close(servfd);servfd = -1;return -1;}return servfd;
}

6. poll

#include "poll_fd_seqlist.h"
#include "fileop_protocol.h"
#include <signal.h>
#include <errno.h>int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog);
int server_main_loop(int fd);int main(int argc,char *argv[]){int servfd = -1;int intport = -1;unsigned short port = 0;if(argc < 3){printf("The argument is too few\n");return 1;}sscanf(argv[2],"%d",&intport);if(intport < 0 || intport > 0xFFFF){printf("The port:%d is invalid\n",intport);return 2;}port = intport;servfd = create_tcp_server_socket(argv[1],port,9);server_main_loop(servfd);close(servfd);servfd = -1;return 0;
}int handle_client(int fd);
int server_main_loop(int fd){int datafd = -1;int ret = 0;int i = 0;struct pollfd_seqlist *psl = create_pollfd_seqlist(20);insert_fd_into_pollfd_seqlist(psl,fd,POLLIN);while(1){clear_all_revents(psl);ret = poll(psl->p_pollfd,psl->cnt,-1);if(ret < 0){if(errno == EINTR)  continue; // because waitelse{printf("Accept error\n");break;}}for(i = 0; i < psl->cnt;i++){if((psl->p_pollfd + i)->revents & POLLIN){if((psl->p_pollfd + i)->fd == fd){datafd = accept(fd,NULL,NULL);insert_fd_into_pollfd_seqlist(psl,datafd,POLLIN);}else{ret = handle_client((psl->p_pollfd + i)->fd);if(ret < 0){remove_fd_from_pollfd_seqlist(psl,(psl->p_pollfd + i)->fd);}}}}}return 0;
}int handle_client(int fd){struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;int flag = 0;do{preq = recv_fileop_pdu(fd);if(preq == NULL){flag = 1;break;}switch(preq->type){case FILE_LEN_REQ:prsp = create_file_len_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;case FILE_TYPE_REQ:prsp = create_file_type_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;default:break;}destroy_fileop_pdu(preq);preq = NULL;}while(0);if(flag){close(fd);fd = -1;return -1;}return 0;
}int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog){int servfd = -1;struct sockaddr_in servaddr;int ret = 0;servfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(svrport);inet_aton(psvrip,&servaddr.sin_addr);ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));ret += listen(servfd,backlog);if(ret){printf("bind or listen failed\n");close(servfd);servfd = -1;return -1;}return servfd;
}

7. epoll

#include "fileop_protocol.h"
#include <signal.h>
#include <errno.h>
#include <sys/epoll.h>int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog);
int server_main_loop(int fd);int main(int argc,char *argv[]){int servfd = -1; int intport = -1; unsigned short port = 0;if(argc < 3){ printf("The argument is too few\n");return 1;}   sscanf(argv[2],"%d",&intport);if(intport < 0 || intport > 0xFFFF){printf("The port:%d is invalid\n",intport);return 2;}   port = intport;servfd = create_tcp_server_socket(argv[1],port,9);server_main_loop(servfd);close(servfd);servfd = -1; return 0;
}#define EVT_MAX 10int handle_client(int fd);
int server_main_loop(int fd){int datafd = -1; int ret = 0;int i = 0;int epollfd = -1; struct epoll_event evt; // evtstruct epoll_event evtarr[EVT_MAX]; // evtarrepollfd = epoll_create(9); // epoll createevt.events = EPOLLIN;evt.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&evt);while(1){ret = epoll_wait(epollfd,evtarr,EVT_MAX,-1); // epoll_waitif(ret < 0){ if(errno == EINTR)  continue;else{printf("select error\n");break;}}for(i = 0;i < ret;i++){if(evtarr[i].events & EPOLLIN){if(evtarr[i].data.fd == fd) {//服务端管理连接用的描述符有数据可读(即有客户端与服务端建立了好逻辑连接)datafd = accept(fd,NULL,NULL); // acceptevt.events = EPOLLIN;evt.data.fd = datafd;epoll_ctl(epollfd,EPOLL_CTL_ADD,datafd,&evt); // add}else{//值为i的描述符是与客户端进行数据交互用的描述符(即accept返回的描述符),其中有数据可读ret = handle_client(evtarr[i].data.fd);if(ret < 0){ epoll_ctl(epollfd,EPOLL_CTL_ADD,datafd,&evt);}}}}}   return 0;
}int handle_client(int fd){struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;int flag = 0;do{ preq = recv_fileop_pdu(fd);if(preq == NULL){flag = 1;break;}switch(preq->type){case FILE_LEN_REQ:prsp = create_file_len_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;case FILE_TYPE_REQ:prsp = create_file_type_rsp(preq->data);if(prsp != NULL){ret = send_fileop_pdu(fd,prsp);destroy_fileop_pdu(prsp);prsp = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);}}else{printf("%s-%d:create_file_len_rsp failed\n",__FILE__,__LINE__);}break;default:break;}destroy_fileop_pdu(preq);preq = NULL;}while(0);if(flag){close(fd);fd = -1; return -1; }   return 0;
}int create_tcp_server_socket(const char *psvrip,unsigned short svrport,int backlog){int servfd = -1; struct sockaddr_in servaddr;int ret = 0;servfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(svrport);inet_aton(psvrip,&servaddr.sin_addr);ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));ret += listen(servfd,backlog);if(ret){printf("bind or listen failed\n");close(servfd);servfd = -1; return -1; }   return servfd;
}

示例输出:

先运行起来服务端:

新建窗口运行客户端:

退出客户端:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/883890.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Qt聊天室客户端】个人信息界面功能开发

1. 加载个人信息 从DataCenter数据类中拿到个人信息相关数据&#xff0c;然后显示到窗口中即可 个人信息界面中所有功能的实现&#xff0c;都是通过按钮触发操作&#xff0c;然后通过网络请求与后台交互完成信息更新 实现 2. 修改昵称 实现逻辑分析 获取输入框的昵称&#xf…

加油-加油

A 时刻注意A可逆&#xff0c;直接除去

十分钟Linux中的epoll机制

epoll机制 epoll是Linux内核提供的一种高效I/O事件通知机制&#xff0c;用于处理大量文件描述符的I/O操作。它适合高并发场景&#xff0c;如网络服务器、实时数据处理等&#xff0c;是select和poll的高效替代方案。 1. epoll的工作原理 epoll通过内核中的事件通知接口和文件…

60V恒流IC SL8443B内置功率MOS 支持2.5A电流 降压LED恒流驱动芯片

一、概述 SL8443B是一款高性能的LED恒流驱动芯片&#xff0c;具有60V的耐压能力&#xff0c;适用于高电压应用场景。它内置了5A&#xff08;或说5V&#xff0c;根据上下文理解为功率等级&#xff09;的功率MOS&#xff0c;可以减少外部元件数量&#xff0c;降低成本&#xff0…

获得淘宝app商品详情原数据 API 券后价获取API

item_get_app_pro-获得淘宝app商品详情原数据 通过此API可以实现通过商品id获取商品详情页数据&#xff0c;包括券后价、主图、详情等等。 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameStr…

得计算题者得天下!软考系统集成计算题详解!

软考中级系统集成项目管理工程师考试一共有《综合知识》和《案例分析》两门科目&#xff0c;而在这两科中都会涉及到计算题&#xff0c;特别是案例分析中&#xff0c;计算题每次考试都会占到一道大题&#xff0c;共25分&#xff0c;占到了科目总分的1/4&#xff0c;所以对于系统…

访问jenkins页面报错

安装fontconfig 即可 yum install fontconfig -y 安装完之后重启jenkins systemctl restart jenkins 再访问

JDBC2(防止sql注入,数据库连接池)

防止SQL注入 sql注入&#xff1a;利用sql语句的语法特点&#xff0c;应用层输入特殊格式&#xff0c;让原有的sql语句失效 创建表结构 并加入数据 create table login(lid int primary key auto_increment,lname varchar(20),lpwd varchar(20),lsex varchar(2),laddr varcha…

SpringBoot多线程

永线程池就不会内存泄漏&#xff0c;否则一直创建线程了&#xff08;如果业务量大很危险了&#xff0c;中断报警…&#xff1f;&#xff09; 【EnableAsync实现异步任务&#xff0c;简单用法】-CSDN博客 两种方法: 1、在启动类上加EnableAsync注解(Async默认的线程池是Simpl…

Linux——五种IO模型

目录 一IO基本理解 二五种IO模型 1五种IO模型示意图 2同步IO和异步IO 二非阻塞IO 1fcntl 2实现非阻塞IO 三多路复用 1select 1.1定位和作用 1.2介绍参数 1.3编写多路复用代码 1.4优缺点 2poll 2.1作用和定位 2.2介绍参数 2.3修改select代码 3epoll 3.1介绍…

[论文阅读]SimCSE: Simple Contrastive Learning of Sentence Embeddings

SimCSE&#xff1a;句子嵌入的简单对比学习 SimCSE: Simple Contrastive Learning of Sentence Embeddings http://arxiv.org/abs/2104.08821 EMNLP 2021 文章介绍了SimCSE&#xff0c;这是一种简单的对比学习框架&#xff0c;采用了自监督来提升模型的句子表示能力 而自监…

【网络原理】——图解HTTPS如何加密(通俗简单易懂)

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;HTTP为什么不安全 二&#xff1a;HTTPS加密过程 1&#xff1a;密码学中的概念 &…

apt的编译安装(古老通讯)

Ubuntu系统的防火墙关闭&#xff1a; ufw disable 第一步&#xff1a;Ubuntu 安装依赖环境 apt -y install libpcre3-dev zlib1g-dev libssl-dev build-essential 如果出现无法下载则在末尾处假如 --fix missing如下图所示 出现下图则为安装成功 第二步&#xff1a; useradd…

上市公司企业数字金融认知数据集(2001-2023年)

一、测算方式&#xff1a;参考C刊《经济学家》王诗卉&#xff08;2021&#xff09;老师的做法&#xff0c;数字金融认知使用每万字年报描述中包含的对数字金融相关关键词的提及次数&#xff0c;关键词为&#xff1a;互联网、数字化、智能、大数据、电子银行、金融科技、科技金融…

LivePortrait——生成可拼接和重定向控制的高效肖像动画翻译

文章目录 原文翻译Abstract1. Introduction2. Related Work2.1. Non-diffusion-based Portrait Animation2.2. Diffusion-based Portrait Animation 3. Methodology3.1. Preliminary of Face Vid2vid3.2. Stage I: Base Model Training3.3. Stage II: Stitching and Retargetin…

快来免费参加交通领域重量级会议:第十三届交通运输研究(上海)论坛

一、会议简介 交通运输研究&#xff08;上海&#xff09;论坛&#xff08;简称为TRF&#xff09;是按照国际会议的组织原则&#xff0c;为综合交通运输领域学者们构建的良好 合作交流平台。交通运输研究&#xff08;上海&#xff09;论坛已经成功举办了十二届&#xff0c;凝聚了…

zabbix 6.0 监控clickhouse(单机)

zabbix 6.0 LTS已经包含了clickhouse的监控模板&#xff0c;所以我们可以直接使用自带的模板来监控clickhouse了。 0.前置条件 clickhouse 已经安装&#xff0c;我安装的是24.3.5.47zabbix-agent 已经安装并配置。系统是ubuntu 2204 server 1. 新建监控用户 使用xml的方式为…

RabbitMQ替换默认端口

前提&#xff1a;客户通过漏洞扫描&#xff0c;发现rabbitmq中的erlang是默认端口4369&#xff0c;出于安全的考虑&#xff0c;需要将erlang的端口修改为其他的端口。 1.查看默认erlang的默认端口 netstat -plnt | grep 4369 2.关闭rabbitmq rabbitmqctl stop&#xff08;注…

Linux上配置NFS服务

架设一台NFS服务器&#xff0c;并按照以下要求配置&#xff1a; 1、开放/nfs/shared目录&#xff0c;供所有用户查询资料&#xff1a; 2、开放/nfs/upload目录&#xff0c;为192.168.xxx.0/24网段主机可以上传目录&#xff0c;并将所有用户及所属的组映射为nfs-upload,其UID和…

MySQL数据库MHA高可用

目录 一、MHA简述 二、MHA 的组成 三、MHA 的特点 四、MHA工作原理 五、MHA部署步骤 六、搭建 MySQL MHA MHA一主两从高可用集群示意图 实验环境 1. Master、Slave1、Slave2 节点上安装 mysql5.7 2. 关闭防火墙 3. 修改 Master、Slave1、Slave2 节点的主机名 4. 修…