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

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

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

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

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

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

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


8.1 基本代码框架

​客户端代码套路

cpp 复制代码
int sockfd = -1;
struct sockaddr_in servaddr;
int ret = 0;

1. 创建一个使用TCP进行传输的引擎对象,并获得该引擎对象的描述符 --- socket
	sockfd = 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. 与服务端建立连接 ------ connect
   ret = connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
   if(ret){
		printf("connect server failed\n");
		......
   }

4. 与服务端进行数据交互(数据传输)  ------ read/write  send/recv
   

5. 无需继续与服务端进行数据传输时,应及时调用close
   close(sockfd);
   sockfd = -1;

服务端代码套路

cpp 复制代码
int connectfd = -1;
int datafd = -1;
struct sockaddr_in servaddr;
int ret = 0;

1. 创建一个使用TCP进行传输的引擎对象,并获得该引擎对象的描述符 --- socket
   connectfd = 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地址和端口号 ------ bind
   ret = bind(connectfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

4. 将第1步的引擎对象变为管理监控连接用的引擎对象 ------listen
   ret += listen(connectfd,9);
   if(ret){
		printf("bind or listen failed\n");
        .......
   }

5. 循环检查有没有客户端与本服务端建立好连接  ------accept
   一旦发现有客户端与本服务端建立好连接就创建一个与该客户端进行数据传输用的引擎对象,并获得该引擎对象的描述符
   利用数据传输用的引擎对象与对应客户端进行数据交互  ----- read/write send/recv
   无需继续与与对应客户端进行数据交互时,应及时关闭数据传输用的引擎对象 ----close
   while(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步修改后的引擎对象 ---- close
   close(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 封装成一个函数

cpp 复制代码
int create_tcp_client_socket(const char *serverip,unsigned short port)

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

cpp 复制代码
int create_tcp_server_socket(const char *serverip,unsigned short port,int backlog)

8.4 服务端并发

三种基本并发方案

1. 多进程并发

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

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

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

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

  1. 多线程并发

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

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

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

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

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

8.4.1 多路复用

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


8.4.2 select


select函数两种形式的使用:

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

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

cpp 复制代码
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 类型的顺序表来管理所有参与监控的描述符和它们的被监控事件

cpp 复制代码
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

cpp 复制代码
#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(不能并发)

cpp 复制代码
#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.多进程

cpp 复制代码
#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 process
    
    while(1){
        datafd = accept(fd,NULL,NULL); // accept
        if(datafd < 0){ 
            if(errno == EINTR)  continue; // because wait
            else{
                printf("Accept error\n");
                break;
            }
        }

        pid = fork(); // fork()
        if(pid < 0){ 
            close(datafd);
            datafd = -1; 
        }

        if(pid == 0){ // son
            close(fd);
            fd = -1; 
            handle_client(datafd);
            exit(0);
        }
        else{ // parents
            close(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. 多线程

cpp 复制代码
#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 detach

    while(1){
        preq = recv_fileop_pdu(fd); // recv pdu
        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;
}

4.粗放式select

cpp 复制代码
#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); // SET

    while(1){
        memcpy(&rfds,&bakrfds,sizeof(fd_set)); // read
        ret = select(FD_SETSIZE,&rfds,NULL,NULL,NULL); //select
        if(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

cpp 复制代码
#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); // SET

    while(1){
        memcpy(&rfds,&bakrfds,sizeof(fd_set)); // read
        maxfd = get_max_fd(&rfds);
        ret = select(maxfd + 1,&rfds,NULL,NULL,NULL); //select
        if(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

cpp 复制代码
#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 wait
            else{
                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

cpp 复制代码
#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 10

int 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; // evt
    struct epoll_event evtarr[EVT_MAX]; // evtarr

    epollfd = epoll_create(9); // epoll create
    evt.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_wait
    
        if(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); // accept
                    evt.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;
}

示例输出:

先运行起来服务端:

新建窗口运行客户端:

退出客户端:

相关推荐
qq_4336184416 分钟前
shell 编程(五)
linux·运维·服务器
VVVVWeiYee1 小时前
项目2路由交换
运维·服务器·网络·网络协议·信息与通信
Zer0_on1 小时前
数据结构栈和队列
c语言·开发语言·数据结构
一只小bit1 小时前
数据结构之栈,队列,树
c语言·开发语言·数据结构·c++
马浩同学2 小时前
【GD32】从零开始学GD32单片机 | DAC数模转换器 + 三角波输出例程
c语言·单片机·嵌入式硬件·mcu
一个没有本领的人2 小时前
win11+matlab2021a配置C-COT
c语言·开发语言·matlab·目标跟踪
一只自律的鸡2 小时前
C项目 天天酷跑(下篇)
c语言·开发语言
小伍_Five3 小时前
透视网络世界:计算机网络习题的深度解析与总结【前3章】
服务器·网络·计算机网络
长安——归故李3 小时前
【C语言】成绩等级制
c语言·开发语言
芷栀夏3 小时前
如何在任何地方随时使用本地Jupyter Notebook无需公网IP
服务器·ide·tcp/ip·jupyter·ip