如何让UDP变得靠谱一些(增加UDP的可靠性)

最近在写一个聊天系统,用到了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;
} 





  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值