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

news2024/11/24 19:49:17

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

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


示例输出:

先运行起来服务端:

新建窗口运行客户端:

退出客户端:

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

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

相关文章

Docker 安装HomeAssistant智能家居系统

HomeAssistant 介绍 简介 Home Assistant是一个自由开源的智能家居自动化平台&#xff0c;它可以控制并监测各种智能家居设备、传感器和其他物联网设备。Home Assistant可以运行在树莓派、NVIDIA Jetson Nano等低功耗设备上&#xff0c;使用户可以快速搭建自己的智能家居系统。…

vim的使用方法

常见的命令可参考&#xff1a; Linux vi/vim | 菜鸟教程​www.runoob.com/linux/linux-vim.html​编辑https://link.zhihu.com/?targethttps%3A//www.runoob.com/linux/linux-vim.html 1. vim的工作模式 vi/vim 共分为三种模式&#xff0c;命令模式、编辑输入模式和末行&am…

java jsoup解析豆瓣电影数据html实战教程

什么是Jsoup Jsoup 是一个开源 Java 库&#xff0c;它提供了一个直观的 API&#xff0c;可使用 DOM API 方法获取 URL 并提取和处理数据。 作为一个多功能库&#xff0c;Jsoup 支持 CSS 选择器和 XPath&#xff0c;它们是识别和选择 HTML 文档中元素的强大选项。 此外&#xf…

CSS 复习

复杂选择器可以通过&#xff08;id的个数&#xff0c;class的个数&#xff0c;标签的个数&#xff09;的形式&#xff0c;计算权重。 如果我们需要将某个选择器的某条属性提升权重&#xff0c;可以在属性后面写!important&#xff1b;注意!importent要写在;前面 很多公司不允许…

计算机毕业设计django+大模型租房推荐系统 租房可视化 租房大屏可视化 租房爬虫 spark 58同城租房爬虫 房源推荐系统

开题报告&#xff1a;《Django大模型租房推荐系统》 一、研究背景与意义 随着城市化进程的加快&#xff0c;房屋租赁市场日益繁荣。然而&#xff0c;传统的房屋租赁方式存在信息不对称、交易流程繁琐等问题&#xff0c;给租户和房主带来了诸多不便。因此&#xff0c;开发一套…

LLM之RAG实战(四十七)| GraphRAG:使用知识图谱改进 RAG 检索策略

在 Retrieval Augmented Generation &#xff08;RAG&#xff09; 技术中&#xff0c;检索是直接影响生成输出质量的关键步骤。然而&#xff0c;基础 RAG 中的向量检索技术通常不足以满足所有情况。例如&#xff0c;传统的检索方法在处理大型私有文档存储库时往往表现不佳。许多…

希亦内衣洗衣机Pro:18项核心数据硬核黑科技,爆发10倍洁净力!

随着人们卫生意识越来越强&#xff0c;小型洗衣机成为热门家电&#xff0c;尤其是对于女士和有婴儿的家庭。近日&#xff0c;洗护领域的佼佼者希亦正式推出了最新款的内衣洗衣机——希亦ACE Pro&#xff0c;为追求精致生活的人们&#xff0c;带来了新的选择&#xff0c;能提供更…

零基础学西班牙语,柯桥专业小语种培训泓畅学校

No te comas el coco, seguro que te ha salido bien la entrevista. Ya te llamarn. 别瞎想了&#xff01;我保证你的面试很顺利。他们会给你打电话的。 这里的椰子是"头"的比喻。在西班牙的口语中&#xff0c;我们也可以听到其他同义表达&#xff0c;比如&#x…

【python】OpenCV—WaterShed Algorithm

文章目录 1、功能描述2、代码实现3、完整代码4、效果展示5、涉及到的库函数5.1、cv2.pyrMeanShiftFiltering5.2、cv2.morphologyEx5.3、cv2.distanceTransform5.4、cv2.normalize5.5、cv2.watershed 6、更多例子7、参考 1、功能描述 基于分水岭算法对图片进行分割 分水岭分割…

微服务设计模式 - 特性标志(Feature Flags)

微服务设计模式 - 特性标志&#xff08;Feature Flags&#xff09; 定义 特性标志&#xff08;Feature Flags&#xff09;&#xff0c;又称特性开关&#xff08;Feature Toggles&#xff09;&#xff0c;是一种常见的云计算设计模式&#xff0c;允许开发人员通过配置动态地打开…

WebStorm EsLint报红色波浪线

如图左侧。 这个错误是由于 ESLint 和 Prettier 的配置不一致导致的。它建议你移除多余的空格。以下是一些解决方法&#xff1a; 安装 Prettier 插件&#xff1a; 确保你在 WebStorm 中安装了 Prettier 插件&#xff0c;并确保它配置正确。 调整 ESLint 配置&#xff1a; 检查…

四、k8s快速入门之Kubernetes资源清单

kubernetes中的资源 ⭐️ k8s中所有的内容都抽象为资源&#xff0c;资源实列化之后&#xff0c;叫做对象 1️⃣名称空间级别 ⭐️ kubeadm在执行k8s的pod的时候会在kube-system这个名称空间下执行&#xff0c;所以说当你kubectl get pod 的时候是查看不到的查看的是默认的po…

数据库->数据库约束

目录 一、数据库约束 1.定义 2.约束类型 3.NOT NULL 非空约束 4. UNIQUE 唯一约束 5.PRIMARY KEY 主键约束 1.主键的使用 2.把表中的主键交给数据库自己维护 2.1主键列设置为null 则使用自增 2.2插入除了主键以外的所有非空列&#xff08;推荐方法&#xff09; 2.3自…

Kafka相关API开发

(一)引入依赖 用API直接去操作kafka(读写数据)在实际开发中用的并不多&#xff0c;学习它主要还是为了加深对Kafka功能的理解。kafka的读写操作&#xff0c;实际开发中&#xff0c;是通过各类更上层的组件去实现。而这些组件在读写kafka数据时&#xff0c;用的当然是kafka的jav…

【K8S系列】Kubernetes 中 NodePort 类型的 Service 无法访问的问题【已解决】

在 Kubernetes 中&#xff0c;NodePort 类型的 Service 允许用户通过每个节点的 IP 地址和指定的端口访问应用程序。如果 NodePort 类型的 Service 无法通过节点的 IP 地址和指定端口进行访问&#xff0c;可能会导致用户无法访问应用。本文将详细分析该问题的常见原因及其解决方…

如何使用AdsPower指纹浏览器克服爬虫技术限制,安全高效进行爬虫!

随着中国开发者日益成熟&#xff0c;应用质量明显提升&#xff0c;越来越多的开发者选择出海寻找机会扩大市场。但“应用出海”说起来容易&#xff0c;做起来难。其中&#xff0c;最大的困恼就是对海外市场缺乏了解。 很多开发者会选择使用网络爬虫&#xff08;Web Crawling&a…

centos7之LVS-DR模式传统部署

介绍 优缺点以及适用场景 优点&#xff1a;能负载更多的Realserver减轻LB的压力,性能高于tun模式。 缺点&#xff1a;不支持端口转发(VIP:80必须代理RIP:80),Realserver和LVS需要在同一网段下。 适用&#xff1a;适用于大多数公司&#xff0c;也是大多数公司用的最多的模式。…

爬虫+数据保存2

爬取数据保存到MySQL数据库 这篇文章, 我们来讲解如何将我们爬虫爬取到的数据, 进行保存, 而且是把数据保存到MySQL数据库的方式去保存。 目录 1.使用pymysql连接数据库并执行插入数据sql代码(insert) 2.优化pymysql数据库连接以及插入功能代码 3.爬取双色球网站的数据并保…

什么样的工程项目管理软件适合中小施工企业?

工程行业是典型的传统行业&#xff0c;劳动密集&#xff0c;协作频繁&#xff0c;依赖经验传承。在工程项目施工过程中&#xff0c;常见的难题纷繁复杂&#xff0c;其中包括效率低下、材料浪费、数据不实、原材料成本上涨、工期延误、质量缺陷和安全风险等。这些问题不仅阻碍了…

机器学习中的嵌入是什么?

一、说明 嵌入是真实世界对象的数字表示&#xff0c;机器学习&#xff08;ML&#xff09;和人工智能&#xff08;AI&#xff09;系统利用它来像人类一样理解复杂的知识领域。例如&#xff0c;计算算法了解 2 和 3 之间的差为 1&#xff0c;这表明与 2 和 100 相比&#xff0c;2…