RDT实现回退N帧协议

RDT实现回退N帧协议

net_exp.h

给出在RDT协议实现中的一些函数和参量的定义

#ifndef NETEXP_H
#define NETEXP_H

#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>


#define     RDT_SERVER_ADDRESS      "127.0.0.1"     //RDT服务器端IP
#define     RDT_RECV_PORT           8003            //RDT接收端端口号
#define     RDT_SEND_PORT           8004            //RDT发送端端口号
#define     RDT_BEGIN_SEQ           1               //RDT数据包初始序列号,(假设数据包序列号不循环)

#define     RDT_PKT_LOSS_RATE       10              //不可靠数据传输层的丢包率
#define     RDT_TIME_OUT            5            //数据包超时时限
#define     RDT_HEADER_LEN          (4 + 4)         //RDT头标长度
#define     RDT_DATA_LEN            1000            //RDT中数据域长度
#define     RDT_PKT_LEN             ( RDT_DATA_LEN + RDT_HEADER_LEN )   //RDT中数据包长度

//RDT包类型
#define     RDT_CTRL_BEGN           0   //初始包
#define     RDT_CTRL_DATA           1   //数据包
#define     RDT_CTRL_ACK            2   //ACK包
#define     RDT_CTRL_NACK           3   //NACK包
#define     RDT_CTRL_END            4   //结束包


/*
    RDT packet format: |CTRL|SEQ|...DATA...|
    将数据封装成RDT数据包,头部只包含控制域和序列号域,其中控制域用来标识RDT数据包类型,序列号域是包含此数据包的序列号。
    函数返回RDT数据包长度
*/
int pack_rdt_pkt( char *data_buf, char *rdt_pkt, int data_len, int seq_num, int flag );

/*
    RDT packet format: |CTRL|SEQ|...DATA...|
    将数据包解封装。
    函数返回RDT包中的数据长度
*/
int unpack_rdt_pkt( char *data_buf, char *rdt_pkt, int pkt_len, int *seq_num, int *flag );

/*
    模拟不可靠数据传输,以一定的概率(RDT_PKT_LOSS_RATE)丢弃数据包,调用形式和recvfrom一致
*/
void udt_sendto( int sock_fd, char *pkt, int pkt_len, int flags, struct sockaddr *recv_addr, int addr_len );

#endif

rdt_pkt_util.c实现net_exp.h中包装的函数

#include "net_exp.h"

/*
    将数据封装成RDT数据包:即在数据前加上RDT数据包头部
*/
int pack_rdt_pkt( char *data_buf, char *rdt_pkt, int data_len, int seq_num, int flag )
{
    char *ptr = rdt_pkt;
    uint32_t ctrl_net_order = htonl( flag );
    uint32_t seq_net_order = htonl( seq_num );

    memcpy( ptr, &ctrl_net_order, sizeof(uint32_t) );
    ptr += sizeof(uint32_t);
    memcpy( ptr, &seq_net_order, sizeof(uint32_t) );
    ptr += sizeof(uint32_t);

    if( data_len > 0 && data_buf != NULL )
        memcpy( ptr, data_buf, data_len );
    return (RDT_HEADER_LEN+data_len);
}

/*
    将RDT数据包解封装
*/
int unpack_rdt_pkt( char *data_buf, char *rdt_pkt, int pkt_len, int *seq_num, int *flag )
{
    char *ptr = rdt_pkt;
    uint32_t ctrl_net_order, seq_net_order;
    int data_len;

    memcpy( &ctrl_net_order, ptr, sizeof(uint32_t) );
    ptr += sizeof(uint32_t);
    *flag = ntohl( ctrl_net_order );

    memcpy( &seq_net_order, ptr, sizeof(uint32_t) );
    ptr += sizeof(uint32_t);
    *seq_num = ntohl( seq_net_order );

    data_len = pkt_len - RDT_HEADER_LEN;
    if( data_buf != NULL && data_len > 0 )
        memcpy( data_buf, ptr, data_len );
    return data_len;
}

/*
    模拟不可靠数据传输,以一定的概率(RDT_PKT_LOSS_RATE)丢弃数据包
*/
void udt_sendto( int sock_fd, char *pkt, int pkt_len, int flags, struct sockaddr *recv_addr, int addr_len )
{
    int seed =  rand() % 100;
    if( seed >= RDT_PKT_LOSS_RATE )
        sendto( sock_fd, pkt, pkt_len, flags, recv_addr, addr_len );
    else //pkt lost
        printf( " emulate packet lost!\n" );
}

发送方rdt_stopwait_sender.c

#include "net_exp.h"

SLD_WIN sending_window; //滑动窗口数据结构体
int total_send_byte = 0; //记录累计发送的字节数   

void usage( char **argv )
{
    printf( "wrong argument!\n" );
    printf( "usage: %s send_file_name. \n",  argv[0] );
}


/*
    处理接收端发回的ACK,重新设置滑动窗口内数据包的状态
*/
void slide_window_ack( int ack_num )
{   
    pthread_mutex_lock( &sending_window.lock );//将互斥量上锁

    printf( "\t[child thread] recv ack# %-8d\n",  ack_num );
    if( ack_num >= sending_window.send_left && ack_num < sending_window.send_right )
    {
        int succ_pkt_seq;
        for( succ_pkt_seq = sending_window.send_left; succ_pkt_seq <= ack_num; succ_pkt_seq++ )
            if( sending_window.rdt_pkts[succ_pkt_seq % sending_window.win_len].state == 1 )
            {
                sending_window.rdt_pkts[succ_pkt_seq % sending_window.win_len].state = 2; //set pkt state to acked
                total_send_byte += (sending_window.rdt_pkts[succ_pkt_seq % sending_window.win_len].pkt_len - RDT_HEADER_LEN);
            }
    }
    else
    {
        if( ack_num < sending_window.send_left )
            printf( "\t[child thread] already acked #pkt %-8d\n", ack_num );
        else
        {
            printf( "\t[child thread] recv wrong acked, #ack[%d] > sending_window.send_right[%d].\n", ack_num, sending_window.send_right );
            sleep(10); //wrong ack, do something?
        }
    }

    pthread_mutex_unlock( &sending_window.lock ); //将互斥量上锁
}

/*
    用于监听端口并接收ACK包的线程函数
*/
void *recv_acks_thread( void *arg )
{
    int *sock_fd = ( int *) arg;
    char rdt_pkt[RDT_PKT_LEN];
    int pkt_len;

    struct sockaddr_in reply_addr;
    int reply_addr_len;
    int reply_ack_seq;
    int reply_ack_flag;
    memset( &reply_addr, 0, sizeof(reply_addr) );
    reply_addr_len = sizeof( reply_addr );

    printf( "\t[child thread] waiting acks..\n" );
    while(1)
    {       
        /*检查是否有数据包到达*/
        pkt_len = recvfrom( *sock_fd, rdt_pkt, RDT_PKT_LEN, MSG_PEEK, 
            (struct sockaddr *)&reply_addr, &reply_addr_len );

        if( pkt_len > 0 ) //有数据包到达
        {
            pkt_len = recvfrom( *sock_fd, rdt_pkt, RDT_PKT_LEN, 0, 
                (struct sockaddr *)&reply_addr, &reply_addr_len   );    

            unpack_rdt_pkt( NULL, rdt_pkt, pkt_len, &reply_ack_seq, &reply_ack_flag );
            if( reply_ack_flag == RDT_CTRL_ACK ) //有ACK包到达
                slide_window_ack( reply_ack_seq ); //重新设置滑动窗口状态
        }
    }
}



/*
    检查滑动窗口内是否有数据包需要发送,返回窗口内待发送的数据包数。
*/
int pre_sending_rdt_pkt( FILE *fp )
{
    int pkt_to_send = 0;

    char read_buf[RDT_DATA_LEN];
    int send_base_acked;

    pthread_mutex_lock( &sending_window.lock );

    STATE_PKT *ptr_pkt_left = &sending_window.rdt_pkts[ sending_window.send_left % sending_window.win_len];
    send_base_acked = (ptr_pkt_left->state == RDT_PKT_ST_ACKED) ? 1 : 0;


    /*
        如果滑动窗口最左端的包已经收到ACK,则将滑动窗口滑动到下一个没收到ACK的包的位置,
        同时将新数据包装入滑动窗口右端空缺位置。
    */
    if( send_base_acked == 1 ) 
    {
        int new_pkt_seq;
        int max_acked;
        max_acked = sending_window.send_left;

        while( max_acked < sending_window.send_right &&
                (sending_window.rdt_pkts[max_acked % sending_window.win_len].state == RDT_PKT_ST_ACKED) )
            max_acked++;
        sending_window.send_left = max_acked;   

        for(    new_pkt_seq = sending_window.send_right; 
                new_pkt_seq < (sending_window.send_left + sending_window.win_len); 
                new_pkt_seq++ )
        {
                if( feof(fp) ) //检查是否已到达发送文件结尾
                    break;

                int read_len, rdt_pkt_len;
                STATE_PKT *ptr_pkt_new = &sending_window.rdt_pkts[new_pkt_seq % sending_window.win_len];

                read_len = fread( read_buf, sizeof(char), RDT_DATA_LEN, fp ); //读取发送文件中的数据
                rdt_pkt_len = pack_rdt_pkt( read_buf, 
                                            ptr_pkt_new->rdt_pkt, 
                                            read_len, 
                                            new_pkt_seq, 
                                            RDT_CTRL_DATA ); //封装为RDT数据包

                //初始化新RDT数据包状态
                ptr_pkt_new->pkt_seq = new_pkt_seq;
                ptr_pkt_new->pkt_len = rdt_pkt_len;
                ptr_pkt_new->state = RDT_PKT_ST_INIT; 
                memset( &(ptr_pkt_new->send_time), 0, sizeof(struct timeval) );

                pkt_to_send++;
        }
        sending_window.send_right = new_pkt_seq;        
    }
    /*
        如果滑动窗口最左端的包还没有收到数据包,则检查其是否超时,如果超时,则重新发送所有滑动窗口内的数据包。否则继续等待。
    */
    else 
    {
        struct timeval time_now;
        gettimeofday( &time_now, NULL );

        /*
            如果是第一次发送数据,将滑动窗口初始化,填满待发送的数据
        */
        if( sending_window.send_left == sending_window.send_right )
        {
            int new_pkt_seq;
            for(    new_pkt_seq = sending_window.send_right; 
                    new_pkt_seq < (sending_window.send_left + sending_window.win_len); 
                    new_pkt_seq++ )
            {
                    int read_len, rdt_pkt_len;
                    if( feof(fp) ) //检查是否已到达发送文件结尾
                        break;
                    STATE_PKT *ptr_pkt_new = &sending_window.rdt_pkts[new_pkt_seq % sending_window.win_len];
                    read_len = fread( read_buf, sizeof(char), RDT_DATA_LEN, fp );
                    rdt_pkt_len = pack_rdt_pkt( read_buf, 
                                                ptr_pkt_new->rdt_pkt, 
                                                read_len, 
                                                new_pkt_seq, 
                                                RDT_CTRL_DATA );
                    ptr_pkt_new->pkt_seq = new_pkt_seq;
                    ptr_pkt_new->pkt_len = rdt_pkt_len;
                    ptr_pkt_new->state = RDT_PKT_ST_INIT; //set pkt state to init state
                    memset( &(ptr_pkt_new->send_time), 0, sizeof(struct timeval) );
                    pkt_to_send++;
            }
            sending_window.send_right = new_pkt_seq;
            printf( "[main thread] begin sending, slide window[%d,%d).\n", 
                            sending_window.send_left, sending_window.send_right );
        }       
        else if( time_out( time_now, ptr_pkt_left->send_time ) ) //第一个数据包超时
        {
            int i; 
            //重新发送窗口内所有数据包
            for( i = sending_window.send_left; i < sending_window.send_right; i++ )
            {
                STATE_PKT *ptr_pkt = &sending_window.rdt_pkts[i % sending_window.win_len];
                memset( &(ptr_pkt->send_time), 0, sizeof(struct timeval) ); 
                ptr_pkt->state = RDT_PKT_ST_TMOUT;
                pkt_to_send++;
            }
            printf( "[main thread] slide window[%d,%d), first pkt time out.\n", 
                        sending_window.send_left, sending_window.send_right );
        }
        else
        {               
            pkt_to_send = 0; //没有超时,继续等待ACK
        }
    }

    pthread_mutex_unlock( &sending_window.lock );

    return pkt_to_send;
}


/*
    Go-Back-N 协议发送端函数
    输入参数:
        send_file_name: 待发送的文件名
        sock_fd:发送数据的socket (同时从该socket发送数据包和接受数据包ACK)
        recv_addr_ptr: 接收端的地址
    说明:
        创建一个子线程用于接收监听端口并接受ACK
        而主线程用于维护滑动窗口并发送数据包
*/

int deliver_file( char *send_file_name, int sock_fd,  struct sockaddr_in *recv_addr_ptr )
{
    char recv_pkt_buf[RDT_PKT_LEN];
    int seq_num = RDT_BEGIN_SEQ;

    struct sockaddr_in reply_addr;
    int reply_addr_len;
    int reply_ack_seq;
    int reply_ack_flag;
    pthread_t worker_thread;
    int reply_thread;

    FILE *fp;
    int i, j, read_len, pkt_len;
    int counter = 1;
    STATE_PKT *ptr_pkt;

    if( (fp = fopen( send_file_name, "r" )) == NULL )
    {
        printf( "open file : %s failed.\n",  send_file_name );
        return 1;
    }

    memset( &reply_addr, 0, sizeof(reply_addr) );

    //初始化互斥量
    pthread_mutex_init( &sending_window.lock, NULL );

    //初始化滑动窗口
    sending_window.win_len = RDT_SENDWIN_LEN;
    sending_window.send_left = RDT_BEGIN_SEQ;
    sending_window.send_right = RDT_BEGIN_SEQ;  //[slide window]= [RDT_BEGIN_SEQ, RDT_BEGIN_SEQ)
    ptr_pkt = &sending_window.rdt_pkts[sending_window.send_left % sending_window.win_len];
    ptr_pkt->pkt_seq = RDT_BEGIN_SEQ;   
    ptr_pkt->state = 0;

    //创建子线程用于接收ACK
    reply_thread = pthread_create( &worker_thread, NULL, recv_acks_thread, (void *)&sock_fd );
    if( reply_thread != 0 )
    {
        perror( "pthread_create failed.\n" );
        exit( 1 );
    }

    while(1)
    {
        int pkt_to_send;

        /*检查滑动窗口内是否有数据包需要发送*/
        pkt_to_send = pre_sending_rdt_pkt( fp );

        if( pkt_to_send > 0 )
        {
            printf( "[main thread] #%d pkts to send.\n", pkt_to_send );
            for( i = sending_window.send_left; i < sending_window.send_right; i++ )
            {
                STATE_PKT *ptr_pkt;
                ptr_pkt = &sending_window.rdt_pkts[ i % sending_window.win_len];
                if( ptr_pkt->state == RDT_PKT_ST_INIT ||  ptr_pkt->state == RDT_PKT_ST_TMOUT )
                {
                    udt_sendto( sock_fd, ptr_pkt->rdt_pkt, ptr_pkt->pkt_len, 0,
                            (struct sockaddr *)recv_addr_ptr, sizeof(*recv_addr_ptr) );

                    ptr_pkt->state = RDT_PKT_ST_SENT;
                    gettimeofday( &ptr_pkt->send_time, NULL );
                    printf( "[main thread] send count #%-8d rdt_pkt #%-8d %-10d bytes.\n", counter++, ptr_pkt->pkt_seq, ptr_pkt->pkt_len );
                }
            }
            printf( "[main thread] slide window: [%d,%d).\n", sending_window.send_left, sending_window.send_right );
        }
        else
        {
            //检查文件是否发送完
            if( sending_window.send_left == sending_window.send_right && feof(fp) ) //data transfer finished
            {
                printf( "[main thread] finished sending! slide window: [%d,%d).\n", sending_window.send_left, sending_window.send_right );
                reply_thread = pthread_cancel( worker_thread ); /*删除子线程*/
                if( reply_thread != 0 )
                {
                    printf( "[main thread] pthread_cancel failed.\n" );
                    exit(1);
                }
                break;          
            }
        }
    }

    //删除互斥量
    pthread_mutex_destroy( &sending_window.lock );

    //结束发送过程,给接受端发送结束数据包(包类型为RDT_CTRL_END)
    if( feof(fp) )
    {
        char rdt_pkt[RDT_PKT_LEN];
        int rdt_pkt_len;
        int new_pkt_seq = sending_window.send_left;
        rdt_pkt_len = pack_rdt_pkt( NULL, 
                            rdt_pkt, 
                            0, 
                            new_pkt_seq, 
                            RDT_CTRL_END );
        sendto( sock_fd, rdt_pkt, rdt_pkt_len, 0, (struct sockaddr *)recv_addr_ptr, sizeof(*recv_addr_ptr) );
    }
    printf( "\n\nsend file %s finished\ntotal send %d bytes.\n", send_file_name, total_send_byte );
    fclose( fp );
    return 0;
}

int main( int argc, char **argv )
{
    struct sockaddr_in recv_addr, send_addr;
    int sock_fd;

    if( argc != 2 )
    {
        usage( argv );
        exit(0);
    }

    srand ( time(NULL) );

    if( ( sock_fd = socket( AF_INET, SOCK_DGRAM, 0 ) ) == -1 )
    {
        printf( "error! information: %s\n", strerror(errno) );
        exit(1);    
    }
    memset( &send_addr, 0, sizeof(send_addr) );
    send_addr.sin_family = AF_INET;
    send_addr.sin_addr.s_addr = htonl( INADDR_ANY );
    send_addr.sin_port = htons( RDT_SEND_PORT );

    if( ( sock_fd = socket( AF_INET, SOCK_DGRAM, 0 ) ) == -1 )
    {
        printf( "error! information: %s\n", strerror(errno) );
        exit(1);    
    }

    //将socket绑定到本地的某个端口,同时从该socket发送数据包和接受ACK
    if( bind( sock_fd, (struct sockaddr *)&send_addr, sizeof(send_addr) ) == -1 )
    {
        close( sock_fd );
        printf( "error! information: %s\n", strerror(errno) );
        exit(1);    
    }

    //设置接收端地址       
    memset( &recv_addr, 0, sizeof(recv_addr) );
    recv_addr.sin_family = AF_INET;
    recv_addr.sin_addr.s_addr = inet_addr( RDT_SERVER_ADDRESS );
    recv_addr.sin_port = htons( RDT_RECV_PORT );


    //调用传输文件的函数
    if( deliver_file( argv[1], sock_fd, &recv_addr ) != 0 )
    {
        printf( "deliver file %s failed.\n", argv[1] );
        close( sock_fd );
        exit(1);
    }

    close( sock_fd );
    return 0;
}


接收方rdt_stopwait_receiver.c

#include "net_exp.h"

void usage( char **argv )
{
    printf( "wrong argument!\n" );
    printf( "usage: %s save_file_name\n",  argv[0] );
}

/*
    Go-Back-N 协议接收端接受函数
    输入参数:
        save_file_name: 保存文件名
        sock_fd:接受数据的socket
*/
int receive_file( char *save_file_name, int sock_fd )
{
    char reply_pkt_buf[RDT_PKT_LEN];
    int reply_pkt_len;

    char rdt_pkt[RDT_PKT_LEN];
    char rdt_data[RDT_DATA_LEN];
    uint32_t seq_net_order;
    int seq_num;
    int flag;
    int exp_seq_num;

    int total_recv_byte = 0;

    struct sockaddr_in client_addr;
    int i, j, sin_len, pkt_len, data_len;

    int counter = 1;
    FILE *fp;

    if( (fp = fopen( save_file_name, "w" )) == NULL )
    {
        printf( "open file : %s failed.\n",  save_file_name );
        return 1;
    }

    memset( &client_addr, 0, sizeof(client_addr) );
    sin_len = sizeof( client_addr );

    exp_seq_num = RDT_BEGIN_SEQ;

    //TODO
    while(1) //接收RDT数据包,直到所有数据全部接收完毕
    {           
        /*
            step 1. 接收RDT数据包 :  recvfrom()
            step 2. 解封装RDT数据包 : unpack_rdt_pkt()
            step 3. 检查此数据包是否为期待的数据包 : seq_num==exp_seq_num
            step 4. 封装一个新的RDT数据包(ACK包) : pack_rdt_pkt()
            step 5. 调用不可靠数据传输发送新的RDT数据包(ACK包): udt_sendto()
        */
        pkt_len = recvfrom(sock_fd, rdt_pkt, RDT_PKT_LEN, 0, (struct sockaddr *) &client_addr,&sin_len);
        int data_len=unpack_rdt_pkt(rdt_data, rdt_pkt, pkt_len, &seq_num, &flag);
        if (flag == RDT_CTRL_END) {
            reply_pkt_len=pack_rdt_pkt(NULL,reply_pkt_buf,0,seq_num,RDT_CTRL_ACK);
           // udt_sendto(sock_fd,reply_pkt_buf,reply_pkt_len,0,(struct sockaddr*)&client_addr,sin_len);
        sendto(sock_fd,reply_pkt_buf,reply_pkt_len,0,(struct sockaddr*)&client_addr,sin_len);
            break;
        } else if (seq_num == exp_seq_num && flag == RDT_CTRL_DATA) {
            exp_seq_num += 1;
            total_recv_byte+=data_len;
            fwrite(rdt_data, sizeof(char),data_len, fp);
            reply_pkt_len=pack_rdt_pkt(NULL,reply_pkt_buf,0,seq_num,RDT_CTRL_ACK);
            //udt_sendto(sock_fd,reply_pkt_buf,reply_pkt_len,0,(struct sockaddr*)&client_addr,sin_len);
        sendto( sock_fd, reply_pkt_buf, reply_pkt_len, 0, (struct sockaddr*)&client_addr, sin_len );
        }
    }
    printf( "\n\nreceive file succeed. write to file %s\ntotal recv %d byte\n", 
                save_file_name, total_recv_byte );

    fflush( fp );
    fclose( fp );
    return 0;
}

int main( int argc, char **argv )
{
    struct sockaddr_in recv_addr;
    int sin_len;
    int sock_fd;
    int pkt_len;

    srand ( time(NULL) );

    if( argc != 2 )
    {
        usage( argv );
        exit(0);
    }

    memset( &recv_addr, 0, sizeof(recv_addr) );
    recv_addr.sin_family = AF_INET;
    recv_addr.sin_addr.s_addr = htonl( INADDR_ANY );
    recv_addr.sin_port = htons( RDT_RECV_PORT );

    if( ( sock_fd = socket( AF_INET, SOCK_DGRAM, 0 ) ) == -1 )
    {
        printf( "error! information: %s\n", strerror(errno) );
        exit(1);    
    }

    if( bind( sock_fd, (struct sockaddr *)&recv_addr, sizeof(recv_addr) ) == -1 )
    {
        close( sock_fd );
        printf( "error! information: %s\n", strerror(errno) );
        exit(1);    
    }

    if( receive_file( argv[1], sock_fd ) != 0 )
    {
        printf( "receive file %s failed.\n", argv[1] );
        close( sock_fd );
        exit(1);
    }

    close( sock_fd );
    return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_38312031/article/details/80519022