tcp网络编程4—并发的io多路复用实现(fcntl非阻塞)

fcnt_vector_fd.h

#ifndef _FCNTL_VECTOR_FD_H
#define _FCNTL_VECTOR_FD_H
typedef struct{
    int *fd;
    int conter;
    int max_conter;
}VectorFd;


extern    VectorFd *create_vector_fd(void);

extern    void      destroy_vector_fd(VectorFd *vfd);

extern    int       get_fd(VectorFd *vfd, int index);

extern    void       remove_fd(VectorFd *vfd, int fd);

extern    void      add_fd(VectorFd *vfd, int fd);

#endif
fcntl_vector_fd.c
#include <stdlib.h>
#include "fcntl_vector_fd.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>

//void *memcpy(void *dest, const void *src, size_t n);


static void expansion_vector(VectorFd *vfd)
{
    
    if(vfd->conter >= vfd->max_conter){
        int *fds = (int *)malloc(vfd->conter + 5*sizeof(int));
        if(fds == NULL){
            perror("expansion:");
            exit(1);
        }
        memcpy(fds, vfd->fd, sizeof(int) * vfd->conter);
        free(vfd->fd);
        vfd->fd = fds;
        vfd->max_conter += 5;    
    }
    return ;
}

static int indexof(VectorFd *vfd, int fd)
{
    int index = 0;
    for(;index < vfd->conter; index++){
        if(vfd->fd[index] == fd){
            return index;
        }
    }    
    return -1;
}

VectorFd *create_vector_fd(void)
{
    VectorFd *vfd = (VectorFd *)malloc(sizeof(VectorFd));
    if(vfd == NULL){
        perror("create:");
        exit(1);
    }
    vfd->fd = (int *)malloc(5*sizeof(int));
    vfd->conter = 0;
    vfd->max_conter = 5;
    
    return vfd;
}

void  destroy_vector_fd(VectorFd *vfd)
{
    if(vfd == NULL){
        perror("destroy:");
        exit(1);
    }
    free(vfd->fd);
    free(vfd);
    
}

int  get_fd(VectorFd *vfd, int index)
{
    if(vfd == NULL){
        perror("get:");
        exit(1);
    }
    if(index < 0 || index > vfd->conter -1){
        return -1;
    }
    return vfd->fd[index];
    
}

void  remove_fd(VectorFd *vfd, int fd)
{
    if(vfd == NULL){
        perror("remove:");
        exit(1);
    }
    int index = indexof(vfd, fd);
    if(index < 0){
        return ;
    }
    for(;index < vfd->conter -1; index++){
        vfd->fd[index] = vfd->fd[index +1];
    }
    
    vfd->fd[vfd->conter-1] = 0;
    
    vfd->conter--;
}


void  add_fd(VectorFd *vfd, int fd)
{
    if(vfd == NULL){
        perror("add:");
        exit(1);
    }
    expansion_vector(vfd);//扩容
    
    vfd->fd[vfd->conter] = fd;
    vfd->conter++;
    return ;
}

 

//fcntl_tcp_server.c

//tcp_fcntl.c

#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include "fcntl_vector_fd.h"

//int socket(int domain, int type, int protocol);


//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);

//void perror(const char *s);

VectorFd *vfd = NULL;

int socket_fd;

void sig_fun(int signo)
{
    if(signo == SIGINT){
        write(STDOUT_FILENO, "signal SIGINT", 15);
        destroy_vector_fd(vfd);
        close(socket_fd);
        exit(1);
    }    
}

static void out_addr(struct sockaddr_in *caddr)
{
    int port = ntohs(caddr->sin_port);
    
    char ip[16];
    memset(ip, 0, sizeof(ip));
    
    if(inet_ntop(AF_INET, &caddr->sin_addr.s_addr, ip ,16) == NULL){
        perror("out_addr:");
    }
    write(STDOUT_FILENO, ip ,sizeof(ip));
    
    return ;
}

//如果客户端往socket中写了数据,子线程在遍历fd时,如果能读到数据,那就将数据作相应的处理,如果读不到将不会阻塞,直接返回
void do_service(int fd)
{
    char buffer[512];    
    memset(buffer, 0, sizeof(buffer));
    /*相当于不完整管道
    *如果服务器从socket中读取到的数据为0,说明客户端关闭了socket或客户端已经挂掉了(服务器读一个写端关闭了的socket)
    *如果服务器往读端(客户端)关闭了的socket中写数据,将会产生一个SIGPIPE的信号,并且errorno被设置为EPIPE(服务器写一个读端关闭了的socket)
    */
    
    //采用非阻塞的read,若读不到数据就直接放回。直接服务于下一个客户端,因此不用判断size小于0的情况
    ssize_t size = read(fd, buffer, sizeof(buffer));    
    
    
    if(size == 0){
        char info[] = "client wtrite closed!";
        write(STDOUT_FILENO, info, sizeof(info));
        //write(STDOUT_FILENO, "client wtrite closed!", 25);
        remove_fd(vfd, fd);
        close(fd);
    }
    else if(size > 0){//为什么lent<0也能进入这个循环? 因为把read的返回值类型写成size_t了,实际上的返回值类型为ssize_t
        write(STDOUT_FILENO, buffer, sizeof(buffer));    
        if(write(fd, buffer, size) <0 ){
            if(errno == EPIPE){
                perror("write");
                remove_fd(vfd, fd);
                close(fd);
            }
            perror("protacal error");
        }
    }
}

void *th_fun(void *arg)
{    
    int i ;
    while(1){
        i = 0;
        for(i = 0; i < vfd->conter; i++){
            do_service(get_fd(vfd, i));
        }
    }
    return (void *)0;
}

int main(int argc,char *argv[])
{
    if(argc <2){
        perror("argc<2:");
        exit(1);
    }
    
    if(signal(SIGINT,sig_fun) == SIG_ERR){
        perror("signal:");
        exit(1);
    }
    
    /*
    *1. 创建socket
    */
    if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("socket:");
        exit(1);
    }
    /*
    *2.绑定IP地址和端口号
    *int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    */
    struct sockaddr_in serviceaddr;
    memset(&serviceaddr, 0, sizeof(serviceaddr));
    serviceaddr.sin_family = AF_INET;
    serviceaddr.sin_port = htons(atoi(argv[1]));
    serviceaddr.sin_addr.s_addr = INADDR_ANY;//一台服务器上可能有多块网卡(多个IP地址)
    //这个宏是响应本机所有网卡(IP地址)上连接的客户端请求
    
    if(bind(socket_fd, (struct sockaddr *)&serviceaddr, sizeof(serviceaddr)) < 0){
        perror("bind:");
        exit(1);
    };
    
    /*
    *3.监听绑定的端口
    *通知系统去监听来自客户端的连接请求
    *(将监听到的客户端连接请求放置到对应的队列中)
    *第二个参数:指定队列的长度
    */
    if(listen(socket_fd, 10) < 0){
        perror("listen:");
        exit(1);
    }
    
    //创建动态数组
    vfd = create_vector_fd();
    
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    pthread_t t;    
    /*
        *5.创建子线程循环遍历动态数组     
        *int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        *                  void *(*start_routine) (void *), void *arg);
    */
    if(pthread_create(&t, &attr, th_fun, (void *)0) != 0){
        perror("pthread_create");
        exit(1);
    }
    pthread_attr_destroy(&attr);
    
    /*
    *4.调用accept函数从队列中获得一个客户端的连接请求
    *并返回一个新的socket描述符,这个描述符和和客户端
    *的连接请求对应,即这个描述符和某个客户端对应
    *如果没有客户端连接,调用此函数会阻塞,直到获得一个客户端的连接
    *第二个参数:客户端的地址信息
    */
    struct sockaddr_in clientaddr;
    socklen_t length = sizeof(clientaddr);
    while(1){
        int fd = accept(socket_fd, (struct sockaddr *)&clientaddr, &length);
        if(fd < 0){
            perror("accept:");
            continue;
        }
        out_addr(&clientaddr);
        
        //将套接字描述符设置为非阻塞的读写
        int flags;
        fcntl(fd, F_GETFL, &flags);
        flags |= O_NONBLOCK;
        if(fcntl(fd, F_SETFL, flags) < 0){
            perror("fcntl:");
            exit(1);
        }
        
        add_fd(vfd, fd);     
    }
    
    return 0;
}

 

//fcntl_tcp_client.c

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int main(int argc, char *argv[])
{
    int socket_fd;
    if(argc < 2){
        printf("参数太少");
        exit(1);
    }
    //1.创建socket
    if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("socket:");
        exit(1);
    }
    
    //2. 连接服务器
    struct sockaddr_in serviceaddr;
    memset(&serviceaddr, 0, sizeof(serviceaddr));
    serviceaddr.sin_family = AF_INET;
    serviceaddr.sin_port = htons(atoi(argv[2]));
    if(inet_pton(AF_INET, argv[1], &serviceaddr.sin_addr.s_addr) < 0){
        perror("inet_pton:");
        exit(1);
    } 
    if(connect(socket_fd, (struct sockaddr *)&serviceaddr, sizeof(serviceaddr)) < 0){
        perror("connect");
        exit(1);
    }
    
    //3. read/write
    char buff[512];
    ssize_t size;
    char *s = ">";
    while(1){
        write(STDOUT_FILENO, s, 1);
        memset(buff, 0, 1024);
        size = read(STDIN_FILENO, buff, sizeof(buff));
        if(size <0 ) continue;
        buff[size -1] = '\0';
        
        if(write(socket_fd, buff, sizeof(buff)) < 0){
            perror("write");
            continue;
        }else{
            if((size1 = read(socket_fd, buff, sizeof(buff))) <0){
                perror("read");
                continue;
            }else{
                printf("%s (%ld)\n",buff, size1);
            }
        }
            
    }
    
    //关闭套接字
    close(socket_fd);
        
    return 0;
}    
    
    

 

posted @ 2023-03-21 23:24  踏浪而来的人  阅读(25)  评论(0编辑  收藏  举报