一、理论部分
TCP的数据通信是全双工通信,也就是可以同时发送和接收数据

发送数据的同时也能够通知对方,之前发送的数据我已经接收到了(ack序号)


TCP流量控制——滑动窗口实现
滑动窗口的发送端
数据分类:① 已发送,未确认 ② 已准备,未发送 ③ 未使用,空闲

滑动窗口的接收端


接收窗口



可以在接收数据时,由接收端主动发包通知窗口变化,或者由发送端定期去主动查询
二、代码实现
tcp_buf结构定义,缓存要发送的数据
data_count:未确认+待发送
unacked_count:未确认字节数
TCP发送队列,先进先服务,是一个循环队列,超过长度要注意回绕
// 定义TCP缓冲区大小
#define XTCP_CFG_RTX_BUF_SIZE 2048
typedef struct _xtcp_buf_t
{
uint16_t data_count, unacked_count;
uint16_t front, tail, next;
uint8_t data[XTCP_CFG_RTX_BUF_SIZE];
}xtcp_buf_t;
TCP控制块添加字段:
struct _xtcp_t
{
xtcp_state_t state;
uint16_t local_port, remote_port;
xipaddr_t remote_ip;
uint32_t unack_seq, next_seq;
uint32_t ack;
uint16_t remote_mss;
uint16_t remote_win;
xtcp_handler_t handler;
xtcp_buf_t tx_buf;
};
在tcp_alloc中对新添加的字段进行初始化:
static xtcp_t* tcp_alloc(void)
{
xtcp_t* tcp, * end;
for (tcp = tcp_socket, end = tcp_socket + XTCP_CFG_MAX_TCP; tcp < end; tcp++)
{
if (tcp->state == XTCP_STATE_FREE)
{
tcp->state = XTCP_STATE_CLOSED;
tcp->local_port = 0;
tcp->remote_port = 0;
tcp->remote_ip.addr = 0;
tcp->handler = (xtcp_handler_t)0;
tcp->remote_win = XTCP_MSS_DEFAULT;
tcp->remote_mss = XTCP_MSS_DEFAULT;
tcp->unack_seq = tcp->next_seq = tcp_get_init_seq();
tcp->ack = 0;
tcp_buf_init(&tcp->tx_buf);
return tcp;
}
}
return (xtcp_t*)0;
}
tcp_buf_init函数实现:
static void tcp_buf_init(xtcp_buf_t* tcp_buf)
{
tcp_buf->tail = tcp_buf->next = tcp_buf->front = 0;
tcp_buf->data_count = tcp_buf->unacked_count = 0;
}
tcp_process_accept函数添加代码:
new_tcp->unack_seq = new_tcp->next_seq = tcp_get_init_seq();
在TCP状态处理中添加数据处理部分代码:
序号只处理unacked_seq,至于next_seq在别的位置去处理
所有非 SYN 的报文都应该带ACK(Windows和Linux都是必须带ACK)
1)既然所有TCP包都有ACK,所以统一先处理ACK回复部分逻辑
2)回复当中优先处理FIN,其次如果有数据发送过来处理数据,如果对方没有传数据过来,判断自己是否有数据需要发送,如果也没有说明对方只是回复,无需进一步处理
remove_header(packet, tcp_hdr->hdr_flags.hdr_len * 4);
switch (tcp->state)
{
case XTCP_STATE_SYNC_RECVD:
{
if (tcp_hdr->hdr_flags.flags & XTCP_FLAG_ACK)
{
tcp->unack_seq++;
tcp->state = XTCP_STATE_ESTABLISHED;
tcp->handler(tcp, XTCP_CONN_CONNECTED);
}
break;
}
case XTCP_STATE_ESTABLISHED:
{
if (tcp_hdr->hdr_flags.flags & (XTCP_FLAG_ACK | XTCP_FLAG_FIN))
{
//优先处理ACK字段
if (tcp_hdr->hdr_flags.flags & XTCP_FLAG_ACK)
{
if ((tcp_hdr->ack > tcp->unack_seq) && (tcp_hdr->ack <= tcp->next_seq))
{
uint16_t curr_ack_size = tcp_hdr->ack - tcp->unack_seq;
tcp_buf_add_acked_count(&tcp->tx_buf, curr_ack_size);
tcp->unack_seq += curr_ack_size;
}
}
if (tcp_hdr->hdr_flags.flags & XTCP_FLAG_FIN)
{
tcp->state = XTCP_STATE_LAST_ACK;
tcp->ack++;
tcp_send(tcp, XTCP_FLAG_FIN | XTCP_FLAG_ACK);
}
else if (tcp_buf_wait_send_count(&tcp->tx_buf))
{
tcp_send(tcp, XTCP_FLAG_ACK);
}
}
break;
}
}
tcp_buf_add_acked_count函数实现:
static void tcp_buf_add_acked_count(xtcp_buf_t* tcp_buf, uint16_t size)
{
tcp_buf->tail += size;
if (tcp_buf->tail >= XTCP_CFG_RTX_BUF_SIZE)
tcp_buf->tail = 0;
tcp_buf->data_count -= size;
tcp_buf->unacked_count -= size;
}
tcp_buf_wait_send_count函数实现:
static void tcp_buf_wait_send_count(xtcp_buf_t* tcp_buf)
{
return tcp_buf->data_count - tcp_buf->unacked_count;
}
TCP数据发送部分处理
1)获取需要发送的字节流
uint16_t data_size = tcp_buf_wait_send_count(&tcp->tx_buf);
2)判断对方窗口是否有空闲(是否可发)
if (tcp->remote_win)
{
data_size = min(data_size, tcp->remote_win);
data_size = min(data_size, tcp->remote_mss);
if (data_size + opt_size > XTCP_DATA_MAX_SIZE)
data_size = XTCP_DATA_MAX_SIZE - opt_size;
}
3)将数据从发送缓冲区放到网络数据包当中
tcp_buf_read_for_send(&tcp->tx_buf, packet->data + opt_size + sizeof(xtcp_hdr_t), data_size);
4)调用ip_out函数将数据包发送出去
err = xip_out(XNET_PROTOCOL_TCP, &tcp->remote_ip, packet);
tcp_send完整代码实现:
#define XTCP_DATA_MAX_SIZE (XNET_CFG_PACKET_MAX_SIZE \
- sizeof(xether_hdr_t) - sizeof(xip_hdr_t) - sizeof(xtcp_hdr_t))
static xnet_err_t tcp_send(xtcp_t* tcp, uint8_t flags)
{
xnet_packet_t* packet;
xtcp_hdr_t* tcp_hdr;
xnet_err_t err;
uint16_t data_size = tcp_buf_wait_send_count(&tcp->tx_buf);
uint16_t opt_size = (flags & XTCP_FLAG_SYN) ? 4 : 0;
//判断当前允许发送的字节数
if (tcp->remote_win)
{
data_size = min(data_size, tcp->remote_win);
data_size = min(data_size, tcp->remote_mss);
if (data_size + opt_size > XTCP_DATA_MAX_SIZE)
data_size = XTCP_DATA_MAX_SIZE - opt_size;
}
else
{
data_size = 0;
}
packet = xnet_alloc_for_send(data_size + opt_size + sizeof(xtcp_hdr_t));
tcp_hdr = (xtcp_hdr_t*)packet->data;
tcp_hdr->src_port = swap_order16(tcp->local_port);
tcp_hdr->dst_port = swap_order16(tcp->remote_port);
tcp_hdr->seq = swap_order32(tcp->next_seq);
tcp_hdr->ack = swap_order32(tcp->ack);
tcp_hdr->hdr_flags.all = 0;
tcp_hdr->hdr_flags.hdr_len = (opt_size + sizeof(xtcp_hdr_t)) / 4;
tcp_hdr->hdr_flags.flags = flags;
tcp_hdr->hdr_flags.all = swap_order16(tcp_hdr->hdr_flags.all);
tcp_hdr->window = 1024;
tcp_hdr->checksum = 0;
tcp_hdr->urgent_ptr = 0;
if (flags & XTCP_FLAG_SYN)
{
uint8_t* opt_data = packet->data + sizeof(xtcp_hdr_t);
opt_data[0] = XTCP_KIND_MSS;
opt_data[1] = 4;
*(uint16_t*)(opt_data + 2) = swap_order16(XTCP_MSS_DEFAULT);
}
tcp_buf_read_for_send(&tcp->tx_buf, packet->data + opt_size + sizeof(xtcp_hdr_t), data_size);
tcp_hdr->checksum = checksum_peso(&netif_ipaddr, &tcp->remote_ip, XNET_PROTOCOL_TCP, (uint16_t*)packet->data, packet->size);
tcp_hdr->checksum = tcp_hdr->checksum ? tcp_hdr->checksum : 0xFFFF;
err = xip_out(XNET_PROTOCOL_TCP, &tcp->remote_ip, packet);
if (err < 0) return err;
tcp->remote_win -= data_size;
tcp->next_seq += data_size;
tcp_buf_add_unacked_count(&tcp->tx_buf, data_size);
if (flags & (XTCP_FLAG_SYN | XTCP_FLAG_FIN))
{
tcp->next_seq++;
}
return XNET_ERR_OK;
}
其中这里设置了三重保障:
1)发送的数据长度 < 对方允许接收的最大数据量(流量控制)
2)发送的数据长度 < 对方的MSS(可避免IP分片导致的数据通信效率下降)(128B以下不分片)
3)发送的数据长度 < 本地MTU
此外,data_size为0,不发送数据不能直接返回,因为还可以只发送标志位
if (tcp->remote_win)
{
data_size = min(data_size, tcp->remote_win);
data_size = min(data_size, tcp->remote_mss);
if (data_size + opt_size > XTCP_DATA_MAX_SIZE)
data_size = XTCP_DATA_MAX_SIZE - opt_size;
}
else
{
data_size=0;
}
tcp_buf_read_for_send函数实现:
其主要功能是将发送缓冲队列当中的数据放到packet当中,准备调用ip_out发送出去
static uint16_t tcp_buf_read_for_send(xtcp_buf_t* tcp_buf, uint8_t* to, uint16_t size)
{
int i;
uint16_t wait_send_count = tcp_buf->data_count - tcp_buf->unacked_count;
size = min(size, wait_send_count);
for (i = 0; i < size; i++)
{
*to++ = tcp_buf->data[tcp_buf->next++];
if (tcp_buf->next >= XTCP_CFG_RTX_BUF_SIZE)
{
tcp_buf->next = 0;
}
}
return size;
}
tcp_buf_add_unacked_count函数实现:
static void tcp_buf_add_unacked_count(xtcp_buf_t* tcp_buf, uint16_t size)
{
tcp_buf->unacked_count += size;
}
在http_server.c当中添加发送数据的逻辑
static uint8_t tx_buffer[1024];
static xnet_err_t http_handler(xtcp_t* tcp, xtcp_conn_state_t state)
{
static char* num = "0123456789ABCDEF";
if (state == XTCP_CONN_CONNECTED)
{
int i;
for (i = 0; i < sizeof(tx_buffer); i++)
{
tx_buffer[i] = num[i % 16];
}
xtcp_write(tcp, tx_buffer, sizeof(tx_buffer));
}
else if (state == XTCP_CONN_CLOSED)
printf("http closed.\n");
return XNET_ERR_OK;
}
xtcp_write函数的定义与实现:
int xtcp_write(xtcp_t* tcp, uint8_t* data, uint16_t size);
xnet_tinny.c
由于此时已经建立TCP连接了,所以所有的数据发送一律添加ACK标签
int xtcp_write(xtcp_t* tcp, uint8_t* data, uint16_t size)
{
uint16_t send_size;
if ((tcp->state != XTCP_STATE_ESTABLISHED))
{
return -1;
}
send_size = tcp_buf_write(&tcp->tx_buf, data.size);
if (send_size)
{
tcp_send(tcp, XTCP_FLAG_ACK);
}
return send_size;
}
其中tcp_buf_write函数实现:
static uint16_t tcp_buf_write(xtcp_buf_t* tcp_buf, uint8_t* from, uint16_t size)
{
int i;
size = min(size, tcp_buf_free_count(tcp_buf));
for (i = 0; i < size; i++)
{
tcp_buf->data[tcp_buf->front++] = *from++;
if (tcp_buf->front >= XTCP_CFG_RTX_BUF_SIZE)
{
tcp_buf->front = 0;
}
}
tcp_buf->data_count += size;
return size;
}
tcp_buf_free_count函数实现:
static uint16_t tcp_buf_free_count(xtcp_buf_t* tcp_buf)
{
return XTCP_CFG_RTX_BUF_SIZE - tcp_buf->data_count;
}
三、测试部分


ok,今天的你就到此为止吧,明天还要接着🐺啊!
3030

被折叠的 条评论
为什么被折叠?



