RDT实现停等协议
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"
void usage( char **argv )
{
printf( "wrong argument!\n" );
printf( "usage: %s send_file_name. \n", argv[0] );
}
/*
停等协议发送端函数
输入参数:
send_file_name: 待发送的文件名,将此文件中的数据封装成一个个的数据包进行发送
sock_fd:发送数据的socket (同时从该socket发送数据包和接收数据包ACK)
recv_addr_ptr: 接收端的地址
*/
int deliver_file( char *send_file_name, int sock_fd, struct sockaddr_in *recv_addr_ptr )
{
char recv_pkt_buf[RDT_PKT_LEN];//4+4+1
char rdt_pkt[RDT_PKT_LEN];//4+4+1
int seq_num = RDT_BEGIN_SEQ;//1
int flag;
int rdt_pkt_len;
int total_send_byte = 0;
struct sockaddr_in reply_addr;
int reply_addr_len;
int reply_ack_seq;
int reply_ack_flag;
char send_window[RDT_DATA_LEN]; //发送端窗口大小为1
FILE *fp;
int i, j, read_len, pkt_len;
int counter = 1;
if( (fp = fopen( send_file_name, "r" )) == NULL )
{
printf( "open file : %s failed.\n", send_file_name );
return 1;
}
while(1)
{
if( feof( fp ) )//如果已经读到发送文件的结尾,则设置数据包类型为RDT_CTRL_END
{
flag = RDT_CTRL_END;
read_len = 0;
rdt_pkt_len = pack_rdt_pkt( NULL, rdt_pkt, 0, seq_num, flag );
}
else //设置数据包类型为RDT_CTRL_DATA
{
flag = RDT_CTRL_DATA;
read_len = fread( send_window, sizeof(char), RDT_DATA_LEN, fp );
rdt_pkt_len = pack_rdt_pkt( send_window, rdt_pkt, read_len, seq_num, flag );
}
while(1) //开始发送
{
fd_set fds; //文件描述符集合
struct timeval timeout;
int sock_state;
printf( "send count #%d, rdt_pkt #%d: %d bytes.\n", counter++, seq_num, rdt_pkt_len );
udt_sendto( sock_fd, rdt_pkt, rdt_pkt_len, 0,
(struct sockaddr *)recv_addr_ptr, sizeof(*recv_addr_ptr) );
timeout.tv_sec = 0;
timeout.tv_usec = RDT_TIME_OUT ;//50000
FD_ZERO( &fds ); //初始化文件描述符
FD_SET( sock_fd, &fds ); //将sock_fd加入到文件描述符集合中
//一直等待到文件描述符集合中某个文件有可读数据,或者到达超时时限
sock_state = select( sock_fd + 1, &fds, NULL, NULL, &timeout );
if( sock_state == -1 ) //socket 错误
{
printf( "select failed.\n" );
return 1;
}
else if( sock_state == 0 ) //数据包超时
{
printf( "packet #%d time out, resend....\n", seq_num );
continue; //重新发送
}
else //文件描述符集合中某个文件有可读数据
{
if( FD_ISSET( sock_fd, &fds ) ) //sock_fd有数据到达
{
memset( &reply_addr, 0, sizeof(reply_addr) );
reply_addr_len = sizeof( reply_addr );
pkt_len = recvfrom( sock_fd, recv_pkt_buf, RDT_PKT_LEN, 0,
(struct sockaddr *)&reply_addr, &reply_addr_len );
unpack_rdt_pkt( NULL, recv_pkt_buf, pkt_len, &reply_ack_seq, &reply_ack_flag );
//发送成功,开始发送下一个数据包
if( reply_ack_seq == seq_num && reply_ack_flag == RDT_CTRL_ACK )
{
printf( "receive ACK for rdt_pkt #%d\n", seq_num);
break; //跳出当前循环
}
if( reply_ack_flag == RDT_CTRL_NACK ) //接收到NACK数据包
continue; //重新发送
}
}
}
seq_num++;
total_send_byte += read_len;
if( flag == RDT_CTRL_END ) //如果所有数据包都发送完,则结束发送
break; //跳出当前循环
}
printf( "\n\nsend file %s finished\ntotal send %5d 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) ); //初始化随机数种子
//UDT套接口sock_fd初始化
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 );
//发送方,本地,8004端口
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 *)&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 );
//接收方,本地,8003端口
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\n", argv[0]);
}
/*
停等协议接收端接收函数
输入参数:
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];
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; //将收到的RDT数据包按顺序写到此文件中
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;// 1
///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;
if (argc != 2) {
usage(argv);
exit(0);
}
srand(time(NULL));
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);
}
printf("receive file succeed.\n");
close(sock_fd);
return 0;
}