上一篇:DIY TCP/IP TCP模块的实现8
9.11 TCP滑动窗口的实现3
9.10节的DIY TCP/IP已经可以正确接收TCP数据帧了。9.10节只是解析一下收到的TCP数据帧携带的数据长度,没有对接收到的数据部分做任何处理。在实现将接收到的TCP数据帧携带的数据放入滑动窗口之前,本节先来实现滑动窗口的初始化。DIY TCP/IP没有编译成动态库被其他应用程序调用,所以需要模拟一个读取线程从滑动窗口中读取已经确认过的TCP数据。
参照9.9节给出的滑动窗口数据结构图,在tcp.c文件中定义TCP滑动窗口数据结构tcp_slide_window_t

   typedef struct _tcp_slide_window {unsigned char *buf;/* receive buffer size */unsigned int buf_sz;/* receive buffer start offset */unsigned int buf_start;/* acknowledged frame pointer (first valid byte) */unsigned int acked_start;/* acknowledged frame pointer (first invalid byte) */unsigned int acked_end;/* received frame (unacked) start pointer (first valid byte )*/unsigned int recv_start;/* received frame (unacked) end pointer (first invalid byte )*/unsigned int recv_end;/* indicate data available to user */pthread_cond_t data_available_cond;/* data available mutex */pthread_mutex_t data_available_mutex;/* receiver simulator */pthread_t usr_simulator;/* stop simulator flag */unsigned char stop_simulator;} tcp_slide_window_t;

buf指向接收缓存的内存空间,buf_sz为接收缓存的大小。将buf看成字节数据组,buf_start, acked_start,acked_end,recv_start和recv_end均为字节数组buf的下标。便于描述将这些下标值称为“指针”。
buf_start指向接收缓存的第一个有效的字节,recv_start指向接收但尚未确认的数据的第一个字节,recv_end指向接收但尚未确认的数据的末尾字节的下一个字节,即接收到的数据按字节顺序从recv_end处开始存放。
acked_start指向可以被读取线程提取的第一个字节,acked_end指向可被提取的末尾字节的下一个字节,即receive start字节处。这些数据组下标随着TCP数据的接收和读取移动,当移动位置超过buf + 16M后,均回绕到buffer start继续开始。回复ack时,16M – (recv_end – acked_start)或16M – (recv_end + 16M – acked_start)即为接收缓存中剩余空间的大小。Sliding window 初始化时,这些数组下标均初始化为0。
应用程序读取TCP接收的数据时,也是读取的某个TCP连接的接收缓存,因此将模拟的读取线程的数据结构放在tcp_slide_window_t数据结构中。data_available_cond是读取线程的睡眠等待条件,data_available_mutex是保护接收缓存的互斥量,避免读取线程与DIY TCP/IP同时修改滑动窗口的“指针”。usr_simulator是模拟的读取线程的pthread_t数据结构,stop_simulator是读取线程退出的条件。
TCP接收缓存与TCP连接对应,即每个TCP连接都有自己的接收缓存。所以还需要修改tcp_conn_t数据结构,添加tcp_slide_window_t成员,即每个TCP连接都有自己的滑动窗口。

  typedef struct _tcp_conn {/* remote port */unsigned short r_port;/* local port */unsigned short l_port;/* remote ip */unsigned char r_ip[4];/* local ip */unsigned char l_ip[4];/* remote max segment size */unsigned short r_mss;/* local mss */unsigned short l_mss;/* remote window size */unsigned short r_wnd_sz;/* local receive window size */unsigned short l_wnd_sz;
…/* connection state */tcp_conn_state_t state;/* sliding window */tcp_slide_window_t sw;} tcp_conn_t;

sw是tcp_conn_t的新增滑动窗口成员,tcp_conn_t数据结构的其余成员与9.4的定义一致,这里不再给出tcp_conn_t数据结构的完整定义。
数据结构修改完后,在tcp.c文件中新增tcp_init_slide_window函数,初始化TCP滑动窗口,该函数在初始化TCP连接的函数tcp_conn_open中被调用。

  static int tcp_init_slide_window(tcp_conn_t *conn){int ret = 0;unsigned int recv_buf_sz = 0;tcp_slide_window_t *sw = NULL;if (conn == NULL)return -1;recv_buf_sz = conn->l_wnd_sz << conn->l_shift_cn;sw = &conn->sw;sw->buf = (unsigned char *)malloc(recv_buf_sz);if (sw->buf == NULL) {log_printf(DEBUG, "No memory ""for tcp receive buffer, %s (%d)\n",strerror(errno), errno);ret = -1;goto out;}log_printf(DEBUG, "TCP init slide window\n");sw->buf_sz = recv_buf_sz;sw->buf_start = 0;sw->acked_start = 0;sw->acked_end = 0;sw->recv_start = 0;sw->recv_end = 0;pthread_cond_init(&sw->data_available_cond, NULL);pthread_mutex_init(&sw->data_available_mutex, NULL);pthread_create(&sw->usr_simulator,NULL, simulate_read_routine, (void *)sw);out:return ret;}

tcp_init_slide_window是TCP模块的静态函数,只在TCP模块内部使用。函数的入参是tcp_conn_t指针conn,指向与滑动窗口对应的TCP连接数据结构。成功返回0,出错返回非0。
Line 10-20: 计算本地TCP连接的接收缓存大小,本地TCP连接的l_wnd_sz和l_shift_cn在tcp_conn_open函数中被初始化为65535和8。此处计算得到接收缓存的大小为16M。申请16M的内存空间,设置滑动窗口sw->buf指向申请到的内存空间。如果申请内存空间失败直接返回。
Line 21-26: 初始化滑动窗口的buf_sz,buf_start,acked_start,acked_end,recv_start和recv_end”指针”,均”指向”字节数组的开始字节。
line 27-35: 初始化模拟读取进程的睡眠等待条件data_available_cond,保护接收缓存的互斥量data_available_mutex,模拟读取进程的函数体simulate_read_routine,读取进程函数体的入参为指向滑动窗口的指针sw。

  static void * simulate_read_routine(void *arg){#define EXPECT_SZ 1460 * 16unsigned char *data_buf = NULL;tcp_slide_window_t *sw = NULL;if (arg == NULL)return NULL;sw = (tcp_slide_window_t *)arg;data_buf = (unsigned char *)malloc(EXPECT_SZ);if (data_buf == NULL) {log_printf(DEBUG, "No memory ""for user data, %s (%d)\n",strerror(errno), errno);return NULL;}log_printf(DEBUG, "User simulator started\n");while(sw->stop_simulator != 1) {pthread_mutex_lock(&sw->data_available_mutex);pthread_cond_wait(&sw->data_available_cond,&sw->data_available_mutex);pthread_mutex_unlock(&sw->data_available_mutex);if (sw->stop_simulator)break;memset(data_buf, 0, EXPECT_SZ);//tcp_export_api_recv(sw, data_buf, EXPECT_SZ);//printf("%s\n", data_buf);}if (data_buf)free(data_buf);log_printf(DEBUG, "User simulator stopped\n");return NULL;}

Line 1-19: 将读取线程函数体的参数转换为滑动窗口tcp_slide_window_t类型,为读取线程申请内存空间,存放读取到的TCP数据,申请内存空间失败时返回NULL。
Line 20-31: 读取线程进入循环体,stop_simulator为1时,退出循环体。进入循环体后,先获取互斥量data_available_mutex,如果接收缓存中没有可用的数据,读取线程睡眠等待在data_available_cond条件的等待队列上。读取线程被唤醒后,释放互斥量,检查是否是要退出循环,如果不是,则读取本地TCP连接的接收缓存中的数据。读取TCP接收缓存数据的代码在9.12节扩展实现,本节只是将读取线程中存放数据的内存空间清零。
line 33-37: 读取线程退出循环体后,释放存放接收TCP数据的内存空间,返回。
tcp_init_slide_window用于初始化滑动窗口,在tcp_conn_open函数中被调用。与其对应的新增函数还有tcp_deinit_slide_window用于销毁滑动窗口,在tcp_deinit函数中销毁TCP连接之前被调用。

 static void tcp_deinit_slide_window(tcp_conn_t *conn){tcp_slide_window_t *sw = NULL;log_printf(DEBUG, "TCP deinit slide window\n");if (conn == NULL)return;sw = &conn->sw;sw->stop_simulator = 1;/* wakeup simulator */pthread_cond_signal(&sw->data_available_cond);pthread_join(sw->usr_simulator, NULL);dump_slide_window(conn);if(sw->buf)free(sw->buf);}

tcp_deinit_slide_window也是TCP模块的静态函数,入参是与滑动窗口对应的TCP连接的指针,无返回值,该函数在销毁TCP模块的函数tcp_deinit中被调用。
line 8 – 15: 设置读取线程的停止条件为1,唤醒读取线程,调用pthread_join等待读取线程退出并回收线程资源。dump_slie_window打印滑动窗口的各个指针的状态,最后释放滑动窗口占用的内存空间。

  static void dump_slide_window(tcp_conn_t *conn){unsigned int acked_start = 0;unsigned int acked_end = 0;unsigned int recv_start = 0;unsigned int recv_end = 0;unsigned int left_sz = 0;unsigned int acked_sz = 0;unsigned int recv_sz = 0;tcp_slide_window_t *sw = NULL;if (conn == NULL)return;sw = &conn->sw;pthread_mutex_lock(&sw->data_available_mutex);acked_start = sw->acked_start;acked_end = sw->acked_end;recv_start = sw->recv_start;recv_end = sw->recv_end;pthread_mutex_unlock(&sw->data_available_mutex);log_printf(DEBUG, "______________________________\n\n");log_printf(DEBUG, "sw->buf_sz: %u\n", sw->buf_sz);log_printf(DEBUG, "sw->buf_start: %u\n", sw->buf_start);log_printf(DEBUG, "sw->acked_start: %u\n", acked_start);log_printf(DEBUG, "sw->acked_end: %u\n", acked_end);log_printf(DEBUG, "sw->recv_start: %u\n", recv_start);log_printf(DEBUG, "sw->recv_end: %u\n", recv_end);log_printf(DEBUG, "______________________________\n");}

dump_slide_window用于debug滑动窗口的实现,首先是获取保护滑动窗口的互斥量,得到滑动窗口的acked_start,acked_end,recv_start和recv_end”指针”位置后,打印出具体的数值。如果仅仅是tcp_deinit_slide_window调用dump_slide_window,就不必获取互斥量,该函数可以在滑动窗口的实现代码的其他函数中也被用到,所以这里需要获取互斥量。
最后修改tcp_conn_open和tcp_deinit函数,添加对tcp_init_slide_window和tcp_deinit_slide_window的调用。

  int tcp_conn_open(unsigned short port){int ret = 0, i;tcp_conn_t *conn = NULL;…conn->state = STATE_LISTEN;conn->l_port = port;conn->l_wnd_sz = 65535;conn->l_shift_cn = 8;conn->l_mss = get_ifmtu(DEFAULT_IFNAME)- sizeof(iphdr_t) - sizeof(tcphdr_t);log_printf(INFO, "TCP listen on port: %u\n", conn->l_port);/* init tcp sliding window */tcp_init_slide_window(conn);out:return ret;}

tcp_conn_open监听指定端口,初始化TCP本地连接,函数的末尾调用tcp_init_slide_window初始化与本地TCP连接对应的滑动窗口。

  void tcp_deinit(){int i = 0;tcp_conn_t *conn = NULL;system("./config.sh "DEFAULT_IFNAME" restore");if (tcp_connections == NULL)return;log_printf(INFO, "Destroy TCP Connections\n");/* deinit tcp slide window */for (i = 0; i < tcp_conn_num; i ++) {conn = &tcp_connections[i];tcp_deinit_slide_window(conn);}free(tcp_connections);}

tcp_deinit函数遍历本地TCP连接表tcp_connections,调用tcp_deinit_slide_window销毁与TCP连接对应的滑动窗口,最后释放TCP连接占用的内存。
本节在9.10节的基础上修改了TCP连接数据结构,添加了TCP滑动窗口的初始化和销毁的代码,实现了模拟读取线程的函数体。本节测试只需检查DIY TCP/IP的运行log,查看读取线程是否启动,销毁时查看滑动窗口的状态信息是否打印。

从DIY TCP/IP的打印输出可以看到,”User simulator started”说明TCP模块的tcp_conn_open函数创建本地TCP连接时成功初始化了TCP滑动窗口。键入ctrl+c结束运行时,打印出滑动窗口信息,说明tcp_deinit_slide_window被正确调用,打印出的滑动窗口的”指针”位置也是符合预期的。
下一篇:DIY TCP/IP TCP模块的实现10

DIY TCP/IP TCP模块的实现9相关推荐

  1. 串口服务器RS485转以太网网口TCP/IP转串口模块导轨式通信网络数据传输通讯设备

    串口服务器RS485转以太网网口TCP/IP转串口模块导轨式通信网络数据传输通讯设备 串口服务器RS485转以太网网口TCP/IP转串口模块导轨式通信网络数据传输通讯设备 导轨型单串口服务器5143D ...

  2. 深入浅析TCP/IP——TCP/IP五层模型

    TCP/IP五层模型 一.TCP/IP五层模型 1.实体层 2.链路层 3.网络层 4.传输层 5.应用层 一.TCP/IP五层模型 层与协议的关系         每一层为了完成一种功能,定义了一种 ...

  3. #TCP/IP# TCP头部选项功能详解

    简单回顾下TCP报文格式 1)TCP报文:由 TCP首部 和 TCP数据 组成. 2)TCP首部:由 20字节的固定长度 和 可变长字段(选项和填充)组成. 3)TCP首部总长度:由TCP头中的&qu ...

  4. [TCP/IP] TCP流和UDP数据报之间的区别

    TCP流和UDP数据报之间的区别 1.TCP本身是面向连接的协议,S和C之间要使用TCP,必须先建立连接,数据就在该连接上流动,可以是双向的,没有边界.所以叫数据流 ,占系统资源多 2.UDP不是面向 ...

  5. [TCP/IP] TCP在listen时的参数backlog的意义

    linux内核中会维护两个队列:   1)未完成队列:接收到一个SYN建立连接请求,处于SYN_RCVD状态   2)已完成队列:已完成TCP三次握手过程,处于ESTABLISHED状态   3)当有 ...

  6. [TCP/IP] TCP建立与终止

    TCP的建立与终止 三次握手与四次挥手 TCP提供一种可靠.面向连接.字节流.传输层的服务.TCP是一种面向连接的单播协议. 一个TCP连接由一个4元组构成,它们分别是源IP地址和源端口号,目的IP地 ...

  7. TCP/IP / TCP 头

    seq:一次 TCP 通信(从 TCP 连接建立到断开)过程中某一个传输方向上的字节流的每一个报文段的编号. ack:A 对 B 发送来的 TCP 报文段的响应,其值是从 B 收到的 TCP 报文段的 ...

  8. [TCP/IP] TCP如何实现流量控制和拥塞控制

    流量控制:数据的传送与接收过程当中很可能出现收方来不及接收的情况,这时就需要对发方进行控制,以免数据丢失.流量控制用于防止在端口阻塞的情况下丢帧,这种方法是当发送或接收缓冲区开始溢出时通过将阻塞信号发 ...

  9. [TCP/IP]TCP服务端accept发生在三次握手的哪一个阶段

    TCP服务端accept发生在三次握手之后 客户端 socket()==>connect()==>write()==>read() 服务端 socket()==>bind()= ...

最新文章

  1. mysql 执行顺序 别名_sql语句的执行顺序和别名问题
  2. android开发学习 ------- 弹出框
  3. 调用摄像头_摄像头 | 浏览器调用摄像头并实现截图保存的效果
  4. Spring Boot Starters启动器
  5. struct ethhdr结构体详解
  6. 浏览器打不开网页,但是还可以聊qq?
  7. Laravel核心代码学习--用户认证系统的实现细节
  8. 非空验证方法(多值)和BindingResult提示验证信息
  9. 【Kafka】kafka Failed to acquire lock on file .lock in /data/kafak-logs a kafka instance
  10. 卷积神经网络在tenserflow的实现
  11. Centos memcached的php拓展 管理界面
  12. 【JAXP】Dom方式解析XML文件
  13. AddressBook 相关操作小计
  14. Unity组件:Lens Flare 镜头光晕
  15. 直接序列扩频通信系统
  16. 踱步狼注释移除,状态机算法更新2019.10.16
  17. 哔哩视频客户端与视频本地化(下载)
  18. Matlab:多项式的四则运算
  19. 不节食 每天运动半小时 减肥成功!
  20. K8s 部署java项目

热门文章

  1. formValidator正则校验密码大小写字母加数字
  2. sanitize---硬盘数据的防护衣
  3. 微星主板刷新BIOS指南
  4. STM32F429移植STemWin,STemWin函数应用
  5. 电流matlab正玄函数,用MATLAB求解线性电路的正弦稳态响应.doc
  6. dataCompare大数据对比之异源数据对比
  7. 大数据技术原理与应用(1-6)-TYUT
  8. PTA 7-207 孔融分梨(函数实现)
  9. python函数返回布尔值_Python_有返回值的函数_布尔函数
  10. 果园机器人能干什么_24* 果园机器人ppt配用优秀获奖教案