rxtx_buffer[ TCP_CHECKSUM_H_P ] = ck.byte.high; rxtx_buffer[ TCP_CHECKSUM_L_P ] = ck.byte.low; // 通过enc28j60发送数据 enc28j60_packet_send ( rxtx_buffer,
ETH_HEADER_LEN+IP_HEADER_LEN+TCP_HEADER_LEN+dlength ); }
3.2 TCP负载长度查询
TCP负载长度查询需要根据IP报文中数据包的总大小和TCP报文中的数据偏移量决定,并需要注意TCP的数据偏移量的单位为双字,即4个字节长度。 [cpp] view plain copy WORD tcp_get_dlength ( BYTE *rxtx_buffer )
{ int dlength, hlength; // 获得IP报文总大小 dlength = ( rxtx_buffer[ IP_TOTLEN_H_P ] <<8 ) | ( rxtx_buffer[ IP_TOTLEN_L_P ] ); // 除去IP报文头部大小 dlength -= IP_HEADER_LEN; // 获得TCP报文数据偏移量 单位为字,需要X4 hlength = (rxtx_buffer[ TCP_HEADER_LEN_P ]>>4) * 4; // 除去TCP报文数据偏移量 dlength -= hlength; if ( dlength <= 0 ) dlength=0; return ((WORD)dlength); }
3.3 TCP负载位置查询 [cpp] view plain copy BYTE tcp_get_hlength ( BYTE *rxtx_buffer ) { // 获得TCP报文数据偏移量 单位为字,需要X4 return ((rxtx_buffer[ TCP_HEADER_LEN_P ]>>4) * 4); // generate len in bytes; } 3.4 TCP负载填充
TCP的负载数据填充和UDP的负载数据填充相似。 [cpp] view plain copy WORD tcp_puts_data ( BYTE *rxtx_buffer, BYTE *data, WORD offset ) { while( *data )
{ rxtx_buffer[ TCP_DATA_P + offset ] = *data++; offset++; } return offset; }
3.5 TCP数据包处理
TCP数据包的处理代码较多,该函数会返回1或0,1代表以太网接收缓冲区的数据被处理,而0代表数据尚未被处理。TCP数据包的处理包含具体的应用实现,该部分出现在服务器端第二次返回ACK之后,具体的代码请结合范例和上文的TCP连接部分。
[cpp] view plain copy BYTE tcp_receive ( BYTE *rxtx_buffer, BYTE *dest_mac, BYTE *dest_ip ) { WORD
tcp_reclen, tcp_sendlen , dest_port; // 获得目标端口号 即客户端端口号 dest_port =
(rxtx_buffer[TCP_SRC_PORT_H_P]<<8)|rxtx_buffer[TCP_SRC_PORT_L_P]; // 匹配TCP协议类型,匹配端口 if ( rxtx_buffer [ IP_PROTO_P ] == IP_PROTO_TCP_V && \\ rxtx_buffer
[ TCP_DST_PORT_H_P ] == TCP_AVR_PORT_H_V && \\ rxtx_buffer
[ TCP_DST_PORT_L_P ] == TCP_AVR_PORT_L_V ) { // 服务器端第1次发送 收到SYN 返回SYN+ACK if ( (rxtx_buffer[ TCP_FLAGS_P ] & TCP_FLAG_SYN_V) ) { tcp_send_packet
( rxtx_buffer, // 发送缓冲区
(WORD_BYTES){dest_port}, // 目标端口号
(WORD_BYTES){TCP_AVR_PORT_V}, // 源端口号 TCP_FLAG_SYN_V|TCP_FLAG_ACK_V, // 标志位 同步位和应答位 1, // 初始化序号,只有在接收到SYN时使用 0, // 不清除确认号 1, //
确认号为上一个数据包的应答编号加1,SYN占用一个序号
0, // 负载长度 dest_mac, // 客户端MAC地址 dest_ip // 客户端IP地
址 ); return 1; } // 收到ACK报文 多种情况 if ( (rxtx_buffer [ TCP_FLAGS_P ] & TCP_FLAG_ACK_V) ) { // 获得TCP负载长度 tcp_reclen =
tcp_get_dlength( rxtx_buffer ); if ( tcp_reclen == 0 ) { // 服务器第4次发送,收到FIN,返回ACK // 主要是为了区别 建立连接时的最后一个ACK if ( (rxtx_buffer[TCP_FLAGS_P] & TCP_FLAG_FIN_V) ) { tcp_send_packet
( rxtx_buffer, // 发送缓冲区
(WORD_BYTES){dest_port}, // 目标端口
(WORD_BYTES){TCP_AVR_PORT_V}, // 源端口 TCP_FLAG_ACK_V, // 标志位 应答 0, // 不操作序号 0, // 不清楚确认号
1, // FIN占一个序号,在一个数据包的序号的基础上累加1
0, // 负载大小
dest_mac, // 客户端MAC地址 dest_ip // 客户端IP地
址 ); } return 1; } // 服务器第2次发送,返回ACK tcp_send_packet
( rxtx_buffer, // 发送缓冲区
(WORD_BYTES){dest_port}, // 目标端口号
(WORD_BYTES){TCP_AVR_PORT_V}, // 源端口号 TCP_FLAG_ACK_V, // 标志位 应答标志 0, // 不操作序号 0, // 不清除确认号 tcp_reclen, //
在上一个数据包的基础上累加tcp_reclen 0, // 负载长度 dest_mac, // 客户端MAC地址 dest_ip ); // 客户端IP地址 // 确定TCP负载位置 WORD tcp_loadpos = tcp_get_hlength( rxtx_buffer) + ETH_HEADER_LEN +
IP_HEADER_LEN; // 复制缓冲区数据