signed

QiShunwang

“诚信为本、客户至上”

【计网实验——prj15】网络传输机制实验二

2021/6/24 21:12:27   来源:

【计网实验——prj15】网络传输机制实验二

实验内容

实验一

  • 运行给定网络拓扑(tcp_topo.py)
  • 在节点h1上执行TCP程序
    • 执行脚本(disable_tcp_rst.sh, disable_offloading.sh),禁止协议栈的相应功能
    • 在h1上运行TCP协议栈的服务器模式 (./tcp_stack server 10001)
  • 在节点h2上执行TCP程序
    • 执行脚本(disable_tcp_rst.sh, disable_offloading.sh),禁止协议栈的相应功能
    • 在h2上运行TCP协议栈的客户端模式,连接h1并正确收发数据 (./tcp_stack client 10.0.0.1 10001)
      • client向server发送数据,server将数据echo给client
  • 使用tcp_stack.py替换其中任意一端,对端都能正确收发数据

实验二

  • 修改tcp_apps.c(以及tcp_stack.py),使之能够收发文件
  • 执行create_randfile.sh,生成待传输数据文件client-input.dat
  • 运行给定网络拓扑(tcp_topo.py)
  • 在节点h1上执行TCP程序
    • 执行脚本(disable_tcp_rst.sh, disable_offloading.sh),禁止协议栈的相应功能
    • 在h1上运行TCP协议栈的服务器模式 (./tcp_stack server 10001)
  • 在节点h2上执行TCP程序
    • 执行脚本(disable_tcp_rst.sh, disable_offloading.sh),禁止协议栈的相应功能
    • 在h2上运行TCP协议栈的客户端模式 (./tcp_stack client 10.0.0.1 10001)
      • Client发送文件client-input.dat给server,server将收到的数据存储到文件server-output.dat
  • 使用md5sum比较两个文件是否完全相同
  • 使用tcp_stack.py替换其中任意一端,对端都能正确收发数据

实现方案

数据接收和缓存

  TCP协议栈收到数据包后,将数据进行缓存,方便应用程序的读取。其中,缓存区域使用的是环形缓存(ring buffer),允许接收的数据缓存大小为recv_window。

tcp_sock_read 函数

  该函数负责读取环形缓存区的内容,同时通过判断缓存区的状态控制数据的接收。当缓存区为空时,阻塞接收数据的线程,进入睡眠状态,等待唤醒;当缓存区不为空时,读取缓存区的内容,并唤醒接收线程(使其能够对读取的数据进行处理),最后将读取数据的长度最为返回值。

int tcp_sock_read(struct tcp_sock *tsk, char *buf, int len) {
	while (ring_buffer_empty(tsk->rcv_buf)) {
		sleep_on(tsk->wait_recv);
	}

	int rlen = read_ring_buffer(tsk->rcv_buf, buf, len);
	wake_up(tsk->wait_recv);
	return rlen;
}

更新tcp_process 函数

  相较于上周的实验,本周的实验需要对该函数进行补充,以支持接收TCP数据包。更新部分的代码如下:

if (tsk->state == TCP_ESTABLISHED) {
    if (tcp->flags & TCP_FIN) {
        tcp_set_state(tsk, TCP_CLOSE_WAIT);
        tsk->rcv_nxt = cb->seq + 1;
        tcp_send_control_packet(tsk, TCP_ACK);
    } else if (tcp->flags & TCP_ACK) {
        if (cb->pl_len == 0) {
            tsk->snd_una = cb->ack;
            tsk->rcv_nxt = cb->seq + 1;
            tcp_update_window_safe(tsk, cb);
        } else {
            while (ring_buffer_full(tsk->rcv_buf)) {
				sleep_on(tsk->wait_recv);
			}
			write_ring_buffer(tsk->rcv_buf, cb->payload, cb->pl_len);
			wake_up(tsk->wait_recv);
			tsk->rcv_nxt = cb->seq + cb->pl_len;
			tsk->snd_una = cb->ack;
			tcp_send_control_packet(tsk, TCP_ACK);
        }
    }

}

  其中,在TCP_ESTABLISHED状态添加实现了处理接收数据的过程,即先确认环形缓存区是否为满,若满,则暂停接收(阻塞接收数据的线程,进入睡眠状态,直到缓存区不再为空时被唤醒);若还未满,则将收到了的数据写入缓存区,将接收线程唤醒。

数据发送

  数据发送的流程如下:

  • 待发送数据全部存储于上层应用buffer中
  • 如果对端recv_window允许,则发送数据
  • 每次读取1个数据包大小的数据
    • min(data_len, 1514 - ETHER_HDR_SIZE - IP_HDR_SIZE - TCP_HDR_SIZE)
  • 封装数据包,通过IP层发送函数,将数据包发出去

  该过程主要通过tcp_sock_write函数实现。

tcp_sock_write 函数

int tcp_sock_write(struct tcp_sock *tsk, char *buf, int len) {
	int single_len = 0;
	int init_seq = tsk->snd_una;
	int init_len = len;

	while (len > 1514 - ETHER_HDR_SIZE - IP_BASE_HDR_SIZE - TCP_BASE_HDR_SIZE) {
		single_len = min(len, 1514 - ETHER_HDR_SIZE - IP_BASE_HDR_SIZE - TCP_BASE_HDR_SIZE);
		int send_packet_len = ETHER_HDR_SIZE + IP_BASE_HDR_SIZE + TCP_BASE_HDR_SIZE + len;
		char * packet = (char *)malloc(send_packet_len);
		memcpy(packet + ETHER_HDR_SIZE + IP_BASE_HDR_SIZE + TCP_BASE_HDR_SIZE, buf + (tsk->snd_una - init_seq), len);
		tsk->snd_wnd = len;
		tcp_send_packet(tsk, packet, send_packet_len);
		if (tsk->snd_wnd == 0) {
			sleep_on(tsk->wait_send);
		}
		len -= single_len;
	}

	int send_packet_len = ETHER_HDR_SIZE + IP_BASE_HDR_SIZE + TCP_BASE_HDR_SIZE + len;
	char * packet = (char *)malloc(send_packet_len);
	memcpy(packet + ETHER_HDR_SIZE + IP_BASE_HDR_SIZE + TCP_BASE_HDR_SIZE, buf + (tsk->snd_una - init_seq), len);
	tsk->snd_wnd = len;
	tcp_send_packet(tsk, packet, send_packet_len);
	return init_len;
}

运行结果

  结点h1作为服务器,结点h2作为客户端,分别运行TCP程序:

实验一

h2运行python tcp_stack.py测试h1

  实验结果如下:

在这里插入图片描述

h1运行python tcp_stack.py测试h2

  实验结果如下:

在这里插入图片描述

h1和h2同时运行本实验中实现的tcp_stack程序

  实验结果如下:

在这里插入图片描述

实验二

h2运行python tcp_stack.py测试h1

  实验结果如下:

在这里插入图片描述

h1运行python tcp_stack.py测试h2

  实验结果如下:

在这里插入图片描述

h1和h2同时运行本实验中实现的tcp_stack程序

  实验结果如下:

在这里插入图片描述
  通过比对可知本次实验结果符合预期,客户端发送的文件与服务器端接受的文件一致。