可靠地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。
#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;
int ready_n;
if ((ready_n = select(c_fd + 1, &rset, &wset, NULL, &tval)) == 0) {
close(c_fd);
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;
}
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;
}
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);
}
}
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));