基于py的网络拓扑网络传输机制实验

发布于:2022-12-13 ⋅ 阅读:(202) ⋅ 点赞:(0)

基于 py 的网络拓扑

实验内容

实验内容一

  • 运行给定网络拓扑(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_sock_read 函数

负责接收 TCP 数据,若 ring buffer 为空,则睡眠,当收到数据包时则被唤醒。具体实现如下:

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;
}
}

handle_recv_data 函数

该函数负责接收 TCP 数据包中的数据,根据 ACK 的值添加进 ring buffer。另外,若 ring buffer 为满,则睡眠。具体实现如下:

void handle_recv_data(struct tcp_sock *tsk, struct tcp_cb *cb) {
	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_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 {
            handle_recv_data(tsk, cb);
        }
    }

}

tcp_sock_write 函数

负责发送 TCP 数据包。如果对端 recv_window 允许,则发送数据,每次读取 1 个数据包大小的数据,即 min(data_len, 1514 - ETHER_HDR_SIZE - IP_HDR_SIZE - TCP_HDR_SIZE)。然后封装数据包,通过 IP 层发送函数,将数据包发出去。具体实现如下:

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);
		send_data(tsk, buf + (tsk->snd_una - init_seq), single_len);
		sleep_on(tsk->wait_send);
		len -= single_len;
	}

	send_data(tsk, buf + (tsk->snd_una - init_seq), len);
	return init_len;
}

其中调用的 send_data 函数如下,负责调用 tcp_send_packet 函数发送数据包:

void send_data(struct tcp_sock *tsk, char *buf, int 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, len);
	tsk->snd_wnd = len;
	tcp_send_packet(tsk, packet, send_packet_len);
}

结果验证

实验一结果

实验结果如下:

在这里插入图片描述

上图可知,可知本次实验结果符合预期。每次能 echo 正确的值。

另外,若 h2 用 tcp_stack.py 运行 client,h1 不管是运行本设计的 server 还是利用用 tcp_stack.py 运行 server,结果都是 echo 出整个字符串“0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”,结果也符合预期。

实验二结果

实验结果如下:

在这里插入图片描述

上图可知,可知本次实验结果符合预期,客户端发送的文件与服务器端接受的文件一致。


网站公告

今日签到

点亮在社区的每一天
去签到