第16章 非阻塞式I/O

执行时间:

        停等版本(完全阻塞)  》  select加阻塞I/O版本  》  fork多进程版本(Linux下多线程也应该差不多)  》  非阻塞I/O版本

非阻塞读写

#include "../Gnet.h"

void do_client(int connfd)
{
    char to[MAX_LINE], fr[MAX_LINE];
    char *toiptr, *tooptr, *friptr, *froptr;
    int val, stdineof, maxfd, n, nwritten;
    fd_set rset, wset;

    val = fcntl(connfd, F_GETFL, 0);
    fcntl(connfd, F_SETFL, val|O_NONBLOCK);
    val = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, val|O_NONBLOCK);
    val = fcntl(STDOUT_FILENO, F_GETFL, 0);
    fcntl(STDOUT_FILENO, F_SETFL, val|O_NONBLOCK);

    toiptr = tooptr = to;
    friptr = froptr = fr;
    stdineof = 0;

    maxfd = MAX(MAX(STDIN_FILENO, STDOUT_FILENO), connfd);
    while(1)
    {
        FD_ZERO(&rset);
        FD_ZERO(&wset);
        if(stdineof==0 && toiptr<&to[MAX_LINE])//标准输入->套接字 还有数据要发送&&缓冲区还有容量
            FD_SET(STDIN_FILENO, &rset);
        if(friptr<&fr[MAX_LINE])//套接字->标准输出 缓冲区还有空闲
            FD_SET(connfd, &rset);
        if(tooptr!=toiptr)
            FD_SET(connfd, &wset);
        if(froptr!=friptr)
            FD_SET(STDOUT_FILENO, &wset);

        select(maxfd+1,&rset,&wset,NULL,NULL);

        if(FD_ISSET(STDIN_FILENO, &rset))
        {
            if((n = read(STDIN_FILENO, toiptr, &to[MAX_LINE]-toiptr)) < 0)
            {
                if(errno != EWOULDBLOCK)
                    perr_exit("read error on stdin");
            }
            else if(n == 0)
            {
                fprintf(stderr,"EOF on stdin\n");
                stdineof = 1;
                if(tooptr == toiptr)
                    shutdown(connfd, SHUT_WR);
            }
            else
            {
                fprintf(stderr, "read %d bytes from stdin\n", n);
                toiptr += n;
                FD_SET(connfd, &wset);
            }
        }

        if(FD_ISSET(connfd, &rset))
        {
            if((n = read(connfd, friptr, &fr[MAX_LINE]-friptr)) < 0)
            {
                if(errno != EWOULDBLOCK)
                    perr_exit("read error on socker");
            }
            else if(n == 0)
            {
                fprintf(stderr, "EOF on socket\n");
                if(stdineof)
                    return;
                else
                    perr_exit("server terminated prematurely");
            }
            else
            {
                fprintf(stderr, "read %d bytes from socket\n", n);
                friptr += n;
                FD_SET(STDOUT_FILENO, &wset);
            }
        }

        if(FD_ISSET(STDOUT_FILENO, &wset) && ((n = friptr-froptr) > 0 ))
        {
            if((nwritten = write(STDOUT_FILENO, froptr, n)) < 0)
            {
                if(errno != EWOULDBLOCK)
                    perr_exit("write error to stdout");
            }
            else
            {
                fprintf(stderr, "wrote %d bytes to stdout\n", nwritten);
                froptr += nwritten;
                if(froptr == friptr)
                    froptr = friptr = fr;
            }
        }

        if(FD_ISSET(connfd, &wset) && ((n = toiptr-tooptr) > 0))
        {
            if((nwritten = write(connfd, tooptr, n)) < 0)
            {
                if(errno != EWOULDBLOCK)
                    perr_exit("write error to socket");
            }
            else
            {
                fprintf(stderr, "wrote %d bytes to socket\n", nwritten);
                tooptr += nwritten;
                if(tooptr == toiptr)
                {
                    toiptr = tooptr = to;
                    if(stdineof)
                        shutdown(connfd, SHUT_WR);
                }
            }
        }
    }
}

    int main(int argc, const char* argv[])
    {
        int connfd;
        struct sockaddr_in server_addr;

        if(argc < 2)
            perr_exit("usage : client <IPaddress>");

        connfd = Socket(AF_INET, SOCK_STREAM, 0);
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
        Connect(connfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

        do_client(connfd);

        return 0;
}

多进程版本

#include "../Gnet.h"

void do_client(int connfd)
{
    char buf[MAX_LINE];
    pid_t pid;

    if((pid = fork()) == 0)
    {
        while(Readline(connfd, buf, MAX_LINE) > 0)
            fputs(buf, stdout);

        kill(getppid(), SIGTERM);
        exit(0);
    }
    else
    {
        while(fgets(buf, MAX_LINE, stdin) != NULL)
            Write(connfd, buf, strlen(buf));

        shutdown(connfd, SHUT_WR);
        pause();
    }
}

int main(int argc, const char* argv[])
{
    int connfd;
    struct sockaddr_in server_addr;

    if(argc < 2)
        perr_exit("usage : client <IPaddress>");

    connfd = Socket(AF_INET, SOCK_STREAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
    Connect(connfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    do_client(connfd);

    return 0;
}

github:https://github.com/gongluck/unp-notes

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ascii0x03的安全笔记

C/C++网络编程时注意的问题小结

1.网络编程在自己定义结构体实现协议的时候,一定要注意字节对齐这个问题。否则sizeof和强制转换指针的时候都会出现很难发现的bug。 什么是字节对齐自行百度。...

3509
来自专栏企鹅号快讯

TCP是否会乱序

问题 TCP客户端发送数据一般这样写 发送数据调用的是write函数,第一个参数是表示socket的文件指针,后面是要传送的数据指针和数据长度。如果数据长度超过...

2496
来自专栏转载gongluck的CSDN博客

初探WinInet网络api

//必须的头文件,而且要链接wininet.lib #include <WinInet.h> #define UM_SOCKET (WM_USER + 1) ...

3305
来自专栏刘望舒

Android网络编程(八)源码解析OkHttp中篇[复用连接池]

1.引子 在了解OkHttp的复用连接池之前,我们首先要了解几个概念。 TCP三次握手 通常我们进行HTTP连接网络的时候我们会进行TCP的三次握手,然后传输数...

26610
来自专栏瓜大三哥

UVM(十一)之各种port续

UVM(十一)之各种port续 UVM中各种port的链接:按照控制流的优先级排序,UVM中三种port为:PORT,EXPORT,IMP。这三种port之间并...

20410
来自专栏Linux驱动

26.Linux-网卡驱动介绍以及制作虚拟网卡驱动(详解)

1.描述 网卡的驱动其实很简单,它还是与硬件相关,主要是负责收发网络的数据包,它将上层协议传递下来的数据包以特定的媒介访问控制方式进行发送, 并将接收到的数据包...

4317
来自专栏开源FPGA

基于FPGA的I2C读写EEPROM

  I2C在芯片的配置中应用还是很多的,比如摄像头、VGA转HDMI转换芯片,之前博主分享过一篇I2C协议的基础学习IIC协议学习笔记,这篇就使用Verilog...

1294
来自专栏小灰灰

linux下socket编程

Socket soket接口是TCP/IP网络的API。网络的socket数据传输是一种特别的I/O,socket也是一种文档描述符。利用socket()函数...

1877
来自专栏用户2442861的专栏

高性能网络编程3----TCP消息的接收

这篇文章将试图说明应用程序如何接收网络上发送过来的TCP消息流,由于篇幅所限,暂时忽略ACK报文的回复和接收窗口的滑动。

821
来自专栏Python

将socket通信变成并发的方式

一 利用multiprocessing模块,开启多进程,实现socket通信并发 1. 开启子进程的两种方式 import time import random...

2838

扫码关注云+社区