梁越

网络编程学习笔记10-第一个netcat的实现(io-multiplexing)

0 人看过

实现简单的netcat,IO复用,视频p21

本笔记参考的视频链接:https://www.bilibili.com/video/BV1Ht411p7wx?p=21
库链接:https://github.com/chenshuo/muduo

##IO模式:阻塞和非阻塞
关于阻塞和非阻塞在另一个文章来介绍

epoll简介

在这之前先了解一下epoll:epoll 全称 eventpoll,是 linux 内核实现IO多路复用(IO multiplexing)的一个实现。IO多路复用的意思是在一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作,和epoll类似的还有poll、select;epoll 监听的 fd(file descriptor)集合是常驻内核的,它有 3 个系统调用 (epoll_create, epoll_wait, epoll_ctl),通过 epoll_wait 可以多次监听同一个 fd 集合,只返回可读写那部分。更详细的介绍在另一个文章

##编译下面的程序

g++ io_multi.cc -o io_multi

##服务端运行

./io_multi  -l 12345

##服务端运行

./io_multi 192.168.200.134 12345
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>  
#include <netinet/in.h> 
#include <unistd.h>
#include <arpa/inet.h>
#include <error.h>
#include <strings.h>
#include <sys/types.h>
#include <pthread.h>
#include <fcntl.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <linux/tcp.h>
#include <signal.h>
#include <arpa/inet.h>
#define MAX 8192
#define SIZE 1024

int setnonblocking(int fd) 
{
    int old = fcntl(fd, F_GETFL);
    int new_one = old | O_NONBLOCK;
    if (fcntl(fd, F_SETFL, new_one) < 0)
    {   
            perror("FCNTL : ");
            return -1; 
    }   

    return old;
}

void addfd(int epollfd, int fd, bool enable_et)
{
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    if (enable_et)
    {
        ev.events |= EPOLLET;
    }
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
    //setnonblocking(fd);
}

void run(int sockfd)
{
    struct epoll_event events[SIZE];
    int epollfd = epoll_create1(0);
    assert(epollfd != -1);
    addfd(epollfd, sockfd, false);
    addfd(epollfd, STDIN_FILENO, false);

    char buf[MAX];
    bool done = false;
    while (!done)
    {
        int ret = epoll_wait(epollfd, events, SIZE, -1);
        for (int i=0; i<ret; ++i)
        {
            int fd = events[i].data.fd;
            if (fd == STDIN_FILENO)
            {
                int nr = read(STDIN_FILENO, buf, sizeof(buf));
                if (nr > 0)
                    send(sockfd, buf, nr, 0);
                else
                {
                    //fix me 
                    close(fd);
                    //shutdown(write)
                    //unrigister stdin 
                }
            }
            else if (fd == sockfd)
            {
                int nr = recv(sockfd, buf, sizeof(buf), 0);
                if (nr > 0)
                    write(STDOUT_FILENO, buf, nr);
                else
                    done = true;
            }
        }
    }
}

int main(int argc, char **argv)
{
        //sigpipe
    signal(SIGPIPE, SIG_IGN);
    if (argc < 3)
    {
        printf("Usage:\n  %s hostname port\n  %s -l port\n", argv[0], argv[0]);
        return 0;
    }

    int port = atoi(argv[2]);

    if (strcmp(argv[1], "-l") == 0)
    {
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        address.sin_port = htons(port);

        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        assert(listenfd >= 0);

        //address reuse and no nagle
        int reuse = 1;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
        setsockopt(listenfd,  IPPROTO_TCP, TCP_NODELAY, &reuse, sizeof(reuse));

        int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
        assert(ret >= 0);

        ret = listen(listenfd,5);
        assert(ret >= 0);
        //printf("Listening...\n"); 

        struct sockaddr_in cli;
        int len = sizeof(cli);
        int sockfd = accept(listenfd, (struct sockaddr*)&cli, (socklen_t*)&len);    
        run(sockfd);
    }
    else
    {
        int sockfd;
        struct sockaddr_in ser;
        bzero(&ser,sizeof(ser));
        sockfd = socket(AF_INET,SOCK_STREAM,0);

        //server

        ser.sin_family = AF_INET;
        ser.sin_addr.s_addr = inet_addr(argv[1]);
        ser.sin_port = htons(12345);
        connect(sockfd, (struct sockaddr*)&ser, sizeof(ser));

        run(sockfd);         
    }

    return 0;
}