最近在写一个聊天系统,用到了UDP协议,但是要给UDP增加一些可靠性,终于在UNP这本书中找到了些许实现的思想,特来写写博客,与大伙交流一下思想,写的不够深入只是实现了书中所写,欢迎拍砖和留言交流!!!!!
UNP书中采用的sigsetjmp 和siglongjmp来避免 由于竟态 导致的SIGALRM
我采用的是pselect中注册了SIGALRM信号进行信号的避免同时还做到了IO多路复用
/*
file_name: udp.h
athor: WK
*/
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<signal.h>
#include<sys/syscall.h>
#include<pthread.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<sys/uio.h>
#include<sys/un.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<time.h>
#define MAXXMT 4
#define MAXLINE 1024
#define RTT_RXTMIN 2
#define RTT_RXTMAN 60
#define RTT_MAXNREXMT 3
struct r_info
{
float r_rtt;
float r_srtt; // 平滑化的rtt 估算因子
float r_rttvar; //平滑化平均偏差估算因子
float r_rto; //重传超时 rto = srtt + 4*rttvar;
int r_nrexmt;
uint32_t r_tbase;
};
void rtt_init(struct r_info *rtt)
{
struct timeval tv;
gettimeofday(&tv, NULL);
rtt->r_tbase = tv.tv_sec;
rtt->r_rtt = 0;
rtt->r_rto = 3;
}
void rtt_newpack(struct r_info *rtt)
{
rtt->r_nrexmt = 0;
}
int rtt_start(struct r_info *rtt)
{
return (int)rtt->r_rto;
}
unsigned rtt_timestamp(struct r_info *rtt)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec-rtt->r_tbase)*1000+(tv.tv_usec)/1000;
}
void rtt_time(struct r_info *rtt, struct timeval *tv)
{
tv->tv_sec = rtt->r_rto;
tv->tv_usec = 0;
return;
}
int rtt_timeout(struct r_info *rtt)
{
rtt->r_rto *= 2;
if (++rtt->r_nrexmt > MAXXMT)
return (-1);
return 0;
}
void rtt_stop(struct r_info *rtt, unsigned sendts)
{
float temp = (rtt_timestamp(rtt)-sendts)/1000;
rtt->r_rtt = rtt->r_rtt*0.9 + temp*0.1;
rtt->r_rto = rtt->r_rtt*2;
}
static int rttinit=0;
static struct r_info rtt;
static struct udphdr
{
unsigned seq; /* sequence */
unsigned ts; /* timestamp/ms */
}hdr_send, hdr_recv;
static void alarm_handler(int signo)
{
return;
}
ssize_t send_recv(int sockfd, void *sendbuff, size_t sendbytes, void *recvbuff, size_t recvbytes, struct sockaddr *praddr, socklen_t len)
{
ssize_t ret;
int maxfd;
struct timeval tv;
fd_set rset;
struct sigaction sa;
sigset_t sigset_alarm, sigset_empty;
struct msghdr msg_send, msg_recv;
struct iovec iov_send[2], iov_recv[2];
if (rttinit == 0)
{
rtt_init(&rtt);
rttinit = 1;
}
memset(&msg_send, 0x00, sizeof(struct msghdr));
memset(&msg_recv, 0x00, sizeof(struct msghdr));
iov_send[0].iov_base = &hdr_send;
iov_send[0].iov_len = sizeof(struct udphdr);
iov_send[1].iov_base = sendbuff;
iov_send[1].iov_len = sendbytes;
iov_recv[0].iov_base = &hdr_recv;
iov_recv[0].iov_len = sizeof(struct udphdr);
iov_recv[1].iov_base = recvbuff;
iov_recv[1].iov_len = recvbytes;
msg_send.msg_name = praddr;
msg_send.msg_namelen = len;
msg_send.msg_iov = iov_send;
msg_send.msg_iovlen = 2;
msg_recv.msg_name = praddr;
msg_recv.msg_namelen = len;
msg_recv.msg_iov = iov_recv;
msg_recv.msg_iovlen = 2;
hdr_send.seq++; /* 该次数据包发送序列号 */
sigemptyset(&sigset_alarm);
sigemptyset(&sigset_empty);
sigaddset(&sigset_alarm, SIGALRM);
sa.sa_handler = alarm_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, NULL) < 0)
{
perror("sigaction");
return -1;
}
rtt_newpack(&rtt);
FD_ZERO(&rset);
while(1)
{
hdr_send.ts = rtt_timestamp(&rtt);
if ((ret = sendmsg(sockfd, &msg_send, 0)) < 0)
{
perror("sendmsg");
return -1;
}
alarm(rtt_start(&rtt));
loop:
FD_SET(sockfd, &rset);
sigprocmask(SIG_BLOCK, &sigset_alarm, NULL);
if ((ret = pselect(sockfd+1, &rset, NULL, NULL, NULL, &sigset_empty)) < 0)
{
if (errno == EINTR)
{
if (rtt_timeout(&rtt) < 0) /* limit */
{
rttinit = 0;
errno = ETIMEDOUT;
return (-1);
}
/* send again */
continue;
}
}
if (FD_ISSET(sockfd, &rset))
{
if ((ret = recvmsg(sockfd, &msg_recv, 0)) < 0)
{
perror("recvmsg");
goto loop;
}
if (ret < sizeof(struct udphdr) || hdr_recv.seq != hdr_send.seq)
{
goto loop;
}
rtt_stop(&rtt, hdr_send.ts);
return ret-sizeof(struct udphdr);
}
}
}
/*
file_name: udp.c
athor: WK
*/
#include"udp.h"
void echo(int sockfd, struct sockaddr *praddr, socklen_t len)
{
int ret;
char sendline[MAXLINE];
char recvline[MAXLINE];
memset(sendline, 0x00, MAXLINE);
while (fgets(sendline, MAXLINE, stdin) != NULL)
{
memset(recvline, 0x00, MAXLINE);
if ((ret = send_recv(sockfd, sendline, strlen(sendline), recvline, MAXLINE, praddr, len)) <=0)
{
printf("perr close\n");
return;
}
recvline[ret] = 0;
fputs(recvline, stdout);
memset(sendline, 0x00, MAXLINE);
}
}
int main(int argc, char *argv[])
{
int connfd;
struct sockaddr_in servaddr;
if ((connfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket");
exit(-1);
}
memset(&servaddr, 0x00, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8002);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
echo(connfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in));
close(connfd);
return 0;
}