网络编程 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 进程间通信 例题

示例代码:

client.c

#include "fileop_protocol.h"char *input_string(char *buf,int size);
int client_main_loop(int fd);
int create_tcp_client_socket(const char *psvrip,unsigned short svrport);int main(int argc,char *argv[]){int fd = -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;fd = create_tcp_client_socket(argv[1],port);client_main_loop(fd);close(fd);fd = -1; return 0;
}int main_ui();
int handle_get_file_len(int fd);
int handle_get_file_type(int fd);int client_main_loop(int fd){int op = -1; int exitflag = 0;while(1){op = main_ui();switch(op){case 1:handle_get_file_len(fd);break;case 2:handle_get_file_type(fd);break;case 0:exitflag = 1;break;}if(exitflag)    break;}return 0;
}int handle_get_file_len(int fd){char filename[80] = ""; struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;printf("Please input a filename:\n");input_string(filename,80);preq = create_file_len_req(filename);ret = send_fileop_pdu(fd,preq);destroy_fileop_pdu(preq);preq = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);return -1; }prsp = recv_fileop_pdu(fd);if(prsp == NULL){printf("%s-%d:recv_fileop_pdu failed\n",__FILE__,__LINE__);return -2; }if(*(int *)prsp->data >= 0){ printf("The len of %s is %d\n",filename,*(int *)prsp->data);}else{printf("Get File Len Failed,error=%d\n",*(int *)prsp->data);}destroy_fileop_pdu(prsp);prsp = NULL;return 0;
}int handle_get_file_type(int fd){char filename[80] = ""; struct fileop_pdu *preq = NULL;struct fileop_pdu *prsp = NULL;int ret = 0;printf("Please input a filename:\n");input_string(filename,80);preq = create_file_type_req(filename);ret = send_fileop_pdu(fd,preq);destroy_fileop_pdu(preq);preq = NULL;if(ret){printf("%s-%d:send_fileop_pdu failed\n",__FILE__,__LINE__);return -1; }prsp = recv_fileop_pdu(fd);if(prsp == NULL){printf("%s-%d:recv_fileop_pdu failed\n",__FILE__,__LINE__);return -2; }switch(*(int *)prsp->data){case REG_FILE:printf("The %s is regular file\n",filename);break;case DIR_FILE:printf("The %s is directory\n",filename);break;case LNK_FILE:printf("The %s is symbol link file\n",filename);break;case CHR_FILE:printf("The %s is char device file\n",filename);break;case BLK_FILE:printf("The %s is block device file\n",filename);break;case FIFO_FILE:printf("The %s is fifo file\n",filename);break;case SOCKET_FILE:printf("The %s is socket file\n",filename);break;case UNKNOW_FILE:printf("The type of %s is unknow\n",filename);break;default:printf("Get File Type Failed,error=%d\n",*(int *)prsp->data);break;}destroy_fileop_pdu(prsp);prsp = NULL;return 0;
}int main_ui(){char buf[12] = ""; int op = -1; printf("Please input your select:\n");printf("1. get a file length\n");printf("2. get a file type\n");printf("0. exit\n");input_string(buf,12);sscanf(buf,"%d",&op);return op; 
}int create_tcp_client_socket(const char *psvrip,unsigned short svrport){int sockfd = -1; struct sockaddr_in servaddr;int ret = 0;sockfd = 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 = connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));if(ret){close(sockfd);sockfd = -1; printf("connect %s-%d failed\n",psvrip,svrport);return -1; }return sockfd;
}

server.c

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.rhkb.cn/news/464705.html

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

相关文章

第二十九章 Vue之插槽

目录 一、引言 二、默认插槽 2.1. 默认插槽基本语法 2.2. 完整代码 2.2.1. main.js 2.2.2. App.vue 2.2.3. MyDialog.vue 2.3. 运行效果 三、插槽后备内容&#xff08;默认值&#xff09; 3.1. 插槽后备内容基本语法 3.2. 完整代码 3.2.1. main.js 3.2.2. App.vu…

宠物领养救助管理软件有哪些功能 佳易王宠物领养救助管理系统使用操作教程

一、概述 佳易王宠物领养救助管理系统V16.0&#xff0c;集宠物信息登记、查询&#xff0c;宠物领养登记、查询&#xff0c; 宠物领养预约管理、货品进出库库存管理于一体的综合管理系统软件。 概述&#xff1a; 佳易王宠物领养救助管理系统V16.0&#xff0c;集宠物信息登记…

【ESP32+MicroPython】开发环境部署

本教程将指导你如何在Visual Studio Code&#xff08;VSCode&#xff09;中设置ESP32的MicroPython开发环境。我们将涵盖从安装Python到烧录MicroPython固件的整个过程&#xff0c;以及如何配置VSCode以便与ESP32进行交互。 准备工作 安装Python 确保你的计算机上安装了Pyth…

前端Nginx的安装与应用

目录 一、前端跨域方式 1.1、CORS(跨域资源共享) 1.2、JSONP(已过时) 1.3、WebSocket 1.4、PostMessage 1.5、Nginx 二、安装 三、应用 四、命令 4.1、基本操作命令 4.2、nginx.conf介绍 4.2.1、location模块 4.2.2、反向代理配置 4.2.3、负载均衡模块 4.2.4、通…

mysql之命令行基础指令

一&#xff1a;安装好mysql后&#xff0c;注册好账号密码。 二&#xff1a;在命令行进行登录的指令如下 mysql -u用户名 -p 例如&#xff1a;mysql -uroot -p; 然后按下回车&#xff0c;进入输入密码。 三&#xff1a;基本指令&#xff1a; 1&#xff1a;查看当前账户的所有…

小白直接冲!BiTCN-BiLSTM-Attention双向时间卷积双向长短期记忆神经网络融合注意力机制多变量回归预测

小白直接冲&#xff01;BiTCN-BiLSTM-Attention双向时间卷积双向长短期记忆神经网络融合注意力机制多变量回归预测 目录 小白直接冲&#xff01;BiTCN-BiLSTM-Attention双向时间卷积双向长短期记忆神经网络融合注意力机制多变量回归预测效果一览基本介绍程序设计参考资料 效果一…

论文概览 |《IJGIS》2024.09 Vol.38 issue9

本次给大家整理的是《International Journal of Geographical Information Science》杂志2024年第38卷第9期的论文的题目和摘要&#xff0c;一共包括9篇SCI论文&#xff01; 论文1 A movement-aware measure for trajectory similarity and its application for ride-sharing …

青少年编程能力等级测评CPA Python编程(一级)

青少年编程能力等级测评CPA Python编程(一级) &#xff08;考试时间90分钟&#xff0c;满分100分&#xff09; 一、单项选择题&#xff08;共20题&#xff0c;每题3.5分&#xff0c;共70分&#xff09; 下列语句的输出结果是&#xff08; &#xff09;。 print(35*2) A&a…

Linux网络命令:它用于实时监控网络接口的状态变化的命令 ip monitor详解

目录 一、概述 二、使用 1、语法 2、对象类型 3、常用选项 4、获取帮助 三、 示例 1. 监视链路层变化 2. 监视所有的网络变化 3. 仅监视路由表的变化 4. 监视特定网络接口的状态变化&#xff1a; 5. 监视网络接口地址的变化 四、实际应用 五、其他事项 一、概述 …

从APP小游戏到Web漏洞的发现

一、前因&#xff1a; 在对一次公司的一个麻将游戏APP进行渗透测试的时候发现&#xff0c;抓到HTTP请求的接口&#xff0c;但是反编译APK后发现没有在本身发现任何一个关于接口或者域名相关的关键字&#xff0c;对此感到了好奇。 于是直接解压后everything搜索了一下&#xff…

【JavaSE】(2) 方法

一、认识方法 1. 方法的定义 修饰符 返回类型 方法名(形参类型 形参名, ......){......return 返回值; } 示例代码&#xff1a; 2. 方法的作用 增强代码的可复用性。&#xff08;避免重复造轮子&#xff09;增强代码的易管理性。&#xff08;改方法就行&#xff0c;不用到处…

柯桥零基础学日语日语培训中为什么不说「ご客様」而是「お客様」?

宝子们是不是经常会看到&#xff0c;很多日语单词前面都有假名「お」或「ご」。 但是又总弄不明白为什么要用「お」、「ご」&#xff0c;用哪个更合适&#xff1f; 今天我们就来好好地扒一扒吧~ 在日语中「お・ご」这样的接头词很常见&#xff0c;一般用来表示美化。 美化语的…

【Linux】简易版shell

文章目录 shell的基本框架PrintCommandLineGetCommandLineParseCommandLineExecuteCommandInitEnvCheckAndExecBuildCommand代码总览运行效果总结 shell的基本框架 要写一个命令行我们首先要写出基本框架。 打印命令行获取用户输入的命令分析命令执行命令 基本框架的代码&am…

Git 概述及相关命令(1)

Git概述 Git是一个强大的分布式版本控制系统&#xff0c;广泛用于代码管理和协作开发。 仓库&#xff08;Repository&#xff09;: 存储项目文件及其历史记录的地方&#xff0c;分为本地仓库和远程仓库。工作区&#xff08;Working Directory&#xff09;: 用户当前工作文件所…

Java栈和队列的快速入门

栈和队列 一、栈 Stack1、概念2、基本操作3、常用方法4、举例5、分析 二、队列1、概念2、常用方法3、举例4、分析&#xff1a; 三、力扣算法快速入门232. 用栈实现队列225. 用队列实现栈 感谢 一、栈 Stack 1、概念 在 Java 中&#xff0c;栈&#xff08;Stack&#xff09;是…

docker 可用镜像服务地址(2024.10.31亲测可用)

1.错误 Error response from daemon: Get “https://registry-1.docker.io/v2/” 原因&#xff1a;镜像服务器地址不可用。 2.可用地址 编辑daemon.json&#xff1a; vi /etc/docker/daemon.json内容修改如下&#xff1a; {"registry-mirrors": ["https://…

【MySQL】深层理解索引及特性(重点)--下(12)

索引&#xff08;重点&#xff09; 1. 索引的作用2. 索引操作2.1 主键索引2.1.1 主键索引的特点2.1.2 创建主键索引 2.2 唯一键索引2.2.1 唯一键索引的特点2.2.2 唯一索引的创建 2.3 普通索引2.3.1 普通索引的特点2.3.2 普通索引的创建 2.4 全文索引2.4.1 全文索引的作用2.4.2 …

临街矩阵乘以自己转置的含义

总结: 临街矩阵* 邻接矩阵转置的(i,j) 位置表示有多少种线路从元素A跳转一条边最终落到元素j的路线. 这个也叫1_degree.

A010-基于SpringBoot的宠物健康咨询系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

DP3复现基础知识(一)—— Hydra 库

DP3 无论是 train 还是 eval 均使用了 Hydra 这一个python 库&#xff0c;这就有些代码在看的时候难以理解其通讯逻辑&#xff0c;例如&#xff1a; hydra.main(version_baseNone,config_pathstr(pathlib.Path(__file__).parent.joinpath(diffusion_policy_3d, config)) ) Hy…