网络编程4-非阻塞IO


  • 9
可靠地udp
https://blog.csdn.net/u011001084/article/details/78977548
滑动窗口观测
https://www.cnblogs.com/my_life/articles/5363527.html

可靠UDP实现
(1)超时重传:用于处理丢失的数据报(重传定时器)
(2)序列号:供客户验证一个应答是否匹配响应的请求。
影响往返时间的因素包括距离,网络速度,拥塞。
需要计算用于发送每个分组的重传超时RTO:需要实测每个RTT时间,更新2个统计估算因子,srtt是平滑化RTT估算因子,rttvar是平滑化平均偏差估算因子

重传二义性的问题
当客户收到重传过的某个请求的一个应答时,不能区分该应答对应于哪一次请求
解决方法:一旦收到重传过的某个请求的一个应答应用一下规则:
即使测得一个RTT也不用它更新估算因子,因为不知道其中的应答对应那次重传的请求;
既然应答在重传定时器期满前到达,当前RTO将继续用于下一个分组,只有当我们收到为崇川过的某个请求的一个应答时,才更新RTT估算因子并重新计算RTO。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
#include <cstdlib>
#include <string>
#include <iostream>

#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <error.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>


using namespace std;

struct so {
    int fd;
    string val;
};

int select_version(int *fd) {
    int c_fd = *fd;
    fd_set rset, wset;
    struct timeval tval;
    FD_ZERO(&rset);
    FD_SET(c_fd, &rset);
    wset = rset;
    tval.tv_sec = 0;
    tval.tv_usec = 300 * 1000; //300毫秒
    int ready_n;
    if ((ready_n = select(c_fd + 1, &rset, &wset, NULL, &tval)) == 0) {
        close(c_fd); /* timeout */
        errno = ETIMEDOUT;
        perror("select timeout.\n");
        return (-1);
    }
    if (FD_ISSET(c_fd, &rset)) {
        int error;
        socklen_t len = sizeof (error);
        if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
            cout << "getsockopt error." << endl;
            return -1;
        }
        cout << "in fire." << error << endl; //in fire . 111 连接被拒绝
    }
    if (FD_ISSET(c_fd, &wset)) {
        int error;
        socklen_t len = sizeof (error);
        if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
            cout << "getsockopt error." << endl;
            return -1;
        }
        cout << "out fire." << error << endl;  //0
    }
    return 0;
}

int epoll_version(int *fd) {
    int c_fd = *fd;
    int ep = epoll_create(1024);
    struct epoll_event event;
    event.events = (uint32_t) (EPOLLIN | EPOLLOUT | EPOLLET);
    struct so _data;
    _data.fd = c_fd;
    _data.val = "test";
    event.data.ptr = (void*) &_data;
    epoll_ctl(ep, EPOLL_CTL_ADD, c_fd, &event);
    struct epoll_event eventArr[1000];
    int status, err;
    socklen_t len;
    err = 0;
    len = sizeof (err);
    int n = epoll_wait(ep, eventArr, 20, 300);
    for (int i = 0; i < n; i++) {
        epoll_event ev = eventArr[i];
        int events = ev.events;
        if (events & EPOLLERR) {
            struct so* so_data = (struct so*) ev.data.ptr;
            cout << so_data->val << ",err event fire." << endl;
            status = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &len);
            cout << status << "," << err << endl;
        }
        if (events & EPOLLIN) {
            struct so* so_data = (struct so*) ev.data.ptr;
            cout << so_data->val << ",in event fire." << endl;
            status = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &len);
            cout << status << "," << err << endl;
        }
        if (events & EPOLLOUT) {
            struct so* so_data1 = (struct so*) ev.data.ptr;
            cout << so_data1->val << ",out event fire." << endl;
        }
    }

}

int main(int argc, char** argv) {
    string ip = "127.0.0.1";
    int port = 25698;
    int c_fd, flags, ret;
    struct sockaddr_in s_addr;
    memset(&s_addr, 0, sizeof (s_addr));
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(port);
    s_addr.sin_addr.s_addr = inet_addr(ip.c_str());

    if ((c_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("create socket fail.\n");
        exit(0);
    }
    flags = fcntl(c_fd, F_GETFL, 0);
    if (flags < 0) {
        perror("get socket flags fail.\n");
        return -1;
    }
    //设置非阻塞模式
    if (fcntl(c_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
        perror("set socket O_NONBLOCK fail.\n");
        return -1;
    }
    ret = connect(c_fd, (struct sockaddr*) &s_addr, sizeof (struct sockaddr));
    while (ret < 0) {
        if (errno == EINPROGRESS) {
            break;
        } else {
            perror("connect remote server fail.\n");
            printf("%d\n", errno);
            exit(0);
        }
    }
    //select_version(&c_fd);
    epoll_version(&c_fd);
    return 0;
}

函数原型:  
int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen);  

功能:用于获取任意类型、任意状态套接口的选项当前值,并将结果存入optval.  

参数:  
      sockfd:标识一个套接口的描述字;  
      level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP;  
      optname:需获取的套接口选项;  
      optval:指针,指向存放所获得选项值的缓冲区;  
      optlen:指针,指向optval缓冲区的长度值;  


返回值:若无错误发生,getsockopt()返回0。否则的话,返回SOCKET_ERROR(-1)错误,应用程序可通过WSAGetLastError()获取相应错误代码。  

当level为SOL_SOCKET时,比较常用到的设置选项如下:  
    1.SO_RCVBUF和SO_SNDBUF:用于设置/读取发送缓冲区和接收缓冲区大小,选项值类型:int,指定新的缓冲区大小,对setsockopt和getsockopt有效;  
       说明:设置缓冲区大小只能在TCP连接建立之前进行,TCP将接收缓冲区大小用于流量控制,UDP不提供流量控制,UDP没有实际的发送缓冲区,设置发送缓冲区的大小将改变能发送的最大UDP数据报的大小,使用如下:  
       int rcv_buf_size = 32 * 1024,snd_buf_size = 32 * 1024;  
      setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(int));  
      setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &SND_buf_size, sizeof(int));   

猜你喜欢

转载自blog.csdn.net/lusic01/article/details/80067562