首页 > 系统相关 >网络编程 TCP编程 Linux环境 C语言实现

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

时间:2024-11-03 19:15:40浏览次数:5  
标签:__ prsp int 编程 TCP C语言 fd ret fileop

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

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进行传输的引擎对象,并获得该引擎对象的描述符 --- 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;

服务端代码套路

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 封装成一个函数

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 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. 多线程

#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

#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

#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

#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

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


示例输出:

先运行起来服务端:

新建窗口运行客户端:

退出客户端:

标签:__,prsp,int,编程,TCP,C语言,fd,ret,fileop
From: https://blog.csdn.net/2301_77329667/article/details/143369774

相关文章

  • 编程小白如何成为大神?大学新生的最佳入门攻略
    编程小白如何成为大神?大学新生的最佳入门攻略如何选择适合自己的编程语言考虑兴趣方向如果对网页开发感兴趣,那么HTML、CSS和JavaScript是很好的选择。HTML用于构建网页的结构,CSS用于网页的样式设计,JavaScript则能为网页添加交互功能。例如,想要制作一个具有动画效果的网站......
  • (2)---【C语言】【GL库】【计算机图形学】DEV C++ 平台openGL库 下的画线图案设计 房
     上篇上手实践  运行结果  实现代码#include<windows.h>#include<GL/glut.h>#defineGLUT_DISABLE_ATEXIT_HACK//处理不同系统宏//星状结构函数,添加了几何中心坐标,半径,叶片宽度占比,叶片数量--------------------------------//绘制内部星状结构基本元素的......
  • 大数据学习笔记 第4天 Shell编程基础高级实战详解
    Shell一、Shell编程概述Shell本身并不是内核的一部分,它只是站在内核的基础上编写的一个应用程序。用户通过Shell来使用Linux,不启动Shell的话,用户就没办法使用Linux。在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(commandinterpr......
  • 少儿编程行业进入成熟期:头部企业构筑壁垒,新入局者面临挑战
    近十年来,随着家长对少儿教育需求的不断增加和政策的支持,中国的少儿编程行业从无到有,从冷门到热门,再到逐步回归理性,经历了一系列的发展与变迁。今天的少儿编程市场正逐渐走向成熟,头部企业构筑了难以撼动的壁垒,而新进入的创业者面临着前所未有的挑战。本文将深入探讨少儿编程行......
  • 科学教育与少儿编程:同向同行,共育新时代科技人才
    科学教育是提升全民科学素质、推动国家科技自立的重要支柱。在新时代背景下,少儿编程不仅是单纯的技术学习,更是科学教育的重要组成部分。通过与科学教育的有机衔接,少儿编程行业正在助力培养具有科学精神和创新能力的青少年。本篇将深入探讨少儿编程与科学教育的协同关系,以及其......
  • 【Spring编程常见错误50例】02.原型bean被固定
    Demo如下所示,通过将ServiceImplScope设置为原型,但是在每次调用接口获取的时候返回的都是同一个实例。显然是不符合我们预期。@RequestMapping(path="/hiScope",method=RequestMethod.GET)publicStringhiScope(){return"hiScope"+serviceImpl......
  • Java 编程:强大的跨平台开发语言
    Java是一种广泛使用的面向对象的编程语言,由SunMicrosystems公司(现已被Oracle公司收购)于1995年发布。Java的设计目标是“一次编写,到处运行”(WriteOnce,RunAnywhere),这使得它成为一种非常强大且灵活的编程语言,尤其适合企业级应用和互联网开发。  Java的特点1.*......
  • C语言:函数
    一.自定义函数自定义函数形式如下:ret_type fun_name(形式参数){}ps:1.ret_type是函数返回类型,有时候是void,表示什么都不返回2.fun_name是函数名,尽量清楚明了3.括号中放的是形式参数,有时候是void,表示没有参数,如果有参数记得加入参数的类型和......