搜索
bottom↓
回复: 61

AVRNET 学习笔记 TCP部分

  [复制链接]

出0入0汤圆

发表于 2013-2-16 00:18:35 | 显示全部楼层 |阅读模式
1.前言
(由于TCP协议复杂,若有解释不对的地方,请毫不客气的提出)
       从实用主义的角度出发,学习嵌入式TCPIP可以直接从本章节开始实施,甚至可以直接从HTTP开始学习。我也曾经是一个现实的实用主义者,以为有了AVRNET项目的源码,修改移植之后便可以用于STM32。但是现实总是那么的残酷,对于一个还布熟悉HTML元素,没有任何PHP或者ASPSOCKET编程知识的我来说,修改AVRNET的任何一行代码都是不可能完成的任务,我几乎不知道修改这行代码造成说明后果。但是通过不断的学习,这些学习包括HTMLCSSJaveScriptPHPMySQLSOCKET等,并不断通过项目积累经验。慢慢的揭开了TCPIP的面纱,而通过TCPAVR返回Hello是多么令人兴奋的事情。本文将通过分析和整理AVRNET项目源码,实现TCP部分内容,并通过TCP打印HelloLCD控制实例说明TCP的使用方法,有了TCP的使用基础,那么实现Web服务器将会是小菜一碟。
       TCP协议是是一个非常复杂的协议,本文并不会实现TCP的方方面面,毫不避讳的说本文提到的TCP是不完整的TCP,没有TCP坚持定时器和TCP重发功能,更别提滑动窗口功能,TCP的传输速度也低的可怜。但是本文通过最简单的代码实现TCP功能,这些包括TCP建立,TCP数据包发送,TCP关闭连接。
1.1 软件平台
硬件平台 Atmega32 + proteus 7.10sp0+WinPcap
编译平台 AVR Studio 6
抓包软件 WireShark
调试工具 网络调试助手
PCIP192.168.1.101
AVR IP 192.168.1.15
AVR TCP默认端口号 3001
1.2 相关资料
ENC28J60学习笔记链接
AVRNET学习笔记 ETHERNETARP部分
AVRNET学习笔记 IPICMP部分
AVRNET学习笔记 UDP部分
AVRNET项目(国外)
AVR webserver项目(国外)

2.TCP协议简述
       TCP是一个负载的协议,详细的内容请参考以下图书。本小节仅介绍TCP如何建立连接,发送数据和终止连接的技巧,本文也不去讲述TCP首部中的种种字节含义,因为图中都有详细的解释。另外也使用很小的篇幅介绍TCP序号和确认号的使用。
1. TCP IP详解1 机械工程出版社出版
2.嵌入式Internet TCP IP基础、实现及应用 北京航空航天出版社
2.1 TCP的建立和终止
       AVRNET项目中暂假定PC机为客户端,AVR为服务器端。在这种情况下,有PC机发起连接,AVR被动的处理整个TCP过程。
1.PC机试图建立连接,发出SYN标志。
2.AVR接收到SYN标志,响应PC机的连接请求,发送SYNACK标志
3.PC机收到SYNACK标志,发送ACK标志,表示TCP连接建立
4.PC机在建立连接之后,向AVR发送TCP负载数据,并包含PUSHACK标志
5.AVR收到负载数据,返回应答数据,并包括FINACKFUSH标志(FIN意味着AVR试图停止本次TCP连接)
6.PC机对负载数据处理,返回ACK标志
7.PC机也发出FIN标志和ACK标志,同意停止本次TCP连接
8.AVR收到该FIN标志,发出ACK标志,意味着本次TCP通信完全结束。
TCP的建立连接,收发数据和停止连接,配合定时器辅以有限状态机是一个常用的方式,但是实现这些功能需要成千上万行代码,通过分析可以看出。
1.对于FIN标志和SYN标志,无论是服务端还是客户端都需要发送ACK标志
2.AVR4次收到ACK标志。第一次为上文的第3步,该ACK位的接收表示TCP连接成功,此时负载数据长度为0AVR对该返回报文不做处理。第二次收到ACK标志,发生在上文的45步,和上次收到ACK不同,负载数据一定不为0。第三次收到ACK标志,发生在第6步,此时负载数据长度也为0AVR对该报文不做任何处理。第4次收到ACK标志发生在第7步,AVR必须应答,但是该报文也含有FIN标志,可以区别于以上几种情况。
       AVRNET项目便采用以上的方式,区别TCP数据包,通过IF ELSE实现了TCP状态机。AVRNET项目先处理SYN标志,接着检查所有的ACK标志,若标志也含有FIN标志,则返回ACK,若负载数据长度为0,不做响应,若负载数据大于0,取出数据做出合适的响应。
2.2 TCP序号和确认号
       TCP序号和确认号也是一个比较微妙的细节。总结起来有以下几点。
1.TCP的初始序号由发送SYN或发送FIN时决定。
2.TCP建立连接时,PC机发送SYN时设置初始序号,AVR返回SYNACK时设置初始序号,这两个初始序号没有任何逻辑上的关系。
3.确认号为上一个数据包的序号加上个数据包的负载长度。
4.SYN标志和FIN特殊,占用个序号,即确认号需在上个数据包的序号基础累加。
       AVRNET项目中何时清零序号,何时初始化序号,何时修改确认号,何时修改确认号的确是一个非常麻烦的过程。通过反复的测试,发现及时确认号出现问题程序也能顺利运行。
3.TCP实现
       从实用主义角度出发,原理不重要重要的是如何实现实现功能。和UDP不同,TCP生成首部时的步骤较多。由于TCP首部中存在选项,所以需要通过更多的代码确定TCP负载的位置,在这里可以分为在TCP首部中查找负载长度字节,并通过首部中的TCP偏移字节确定长度
3.1 TCP数据发送
       TCP发送时的参数很多,大致可以分为以下步骤。
1.确定下一个确认号的具体值。使用时有两种情况,那么累加1,要么累加上一个报文的负载长度。
2.初始化序号,可以在ABRNET作为客户端时使用
3.清除序号,可以在TCP建立连接时使用
4.设置标志,例如FIN SYN
5.设置其他参数,例如紧急指针,窗口大小
6.填充以太网首部,IP首部
7.加入校验和
  1. void tcp_send_packet (
  2.   BYTE *rxtx_buffer,          //发送缓冲区
  3.   WORD_BYTES dest_port,       // 目标端口号
  4.   WORD_BYTES src_port,        // 源端口号
  5.   BYTE flags,                 // TCP标志 FIN SYN ACK
  6.   BYTE max_segment_size,      // 初始化序号 接收SYN时使用
  7.   BYTE clear_seqack,          // 设置确认号为0 发送SYN时使用
  8.   WORD next_ack_num,          // 在上一个数据包序号的基础上累加
  9.   WORD dlength,               // 负载长度
  10.   BYTE *dest_mac,             // 目标MAC地址
  11.   BYTE *dest_ip )             // 目标IP地址     
  12. {
  13.   BYTE i, tseq;
  14.   WORD_BYTES ck;

  15.   // 生成以太网报文头
  16.   eth_generate_header ( rxtx_buffer, (WORD_BYTES){ETH_TYPE_IP_V}, dest_mac );  

  17.   // 计算数据包 确认号 next_ack_num为累加值
  18.   // 1.确认号因等于上一个数据包的序号加上数据包长度
  19.   // 2.序号等于上一个数据包的确认号
  20.   // 3.FIN和SYN各占一个序号
  21.   // 4.确认号和序号使用大端模式,即高地址存放低位数据
  22.   // 5.确认号修改发生在三种情况,接收到SYN,接收到FIN,接收到负载数据
  23.   if ( next_ack_num )
  24.   {
  25.     for( i = 4 ; i > 0; i-- )
  26.     {
  27.       // 取出上一个数据包的序号,累加next_ack_num
  28.       next_ack_num = rxtx_buffer [ TCP_SEQ_P + i - 1] + next_ack_num;
  29.       // 取出上一个数据包的确认号
  30.       tseq = rxtx_buffer [ TCP_SEQACK_P + i - 1];
  31.       // 复制本次数据包的确认号,即上个数据包的序号+next_ack_num
  32.       rxtx_buffer [ TCP_SEQACK_P + i - 1] = 0xff & next_ack_num;
  33.       // 复制上一个数据包的确认号于本数据包的序号
  34.       rxtx_buffer[ TCP_SEQ_P + i - 1 ] = tseq;
  35.      
  36.       next_ack_num >>= 8;
  37.     }
  38.   }

  39.   // 初始化序号
  40.   // 设置最大分片
  41.   // 第一次发送或接收时使用
  42.   if ( max_segment_size )
  43.   {
  44.     // 初始化序号
  45.     rxtx_buffer[ TCP_SEQ_P + 0 ] = 0;
  46.     rxtx_buffer[ TCP_SEQ_P + 1 ] = 0;
  47.     rxtx_buffer[ TCP_SEQ_P + 2 ] = seqnum;
  48.     rxtx_buffer[ TCP_SEQ_P + 3 ] = 0;
  49.     seqnum += 2;

  50.     // 初始化 报文段最大长度
  51.     rxtx_buffer[ TCP_OPTIONS_P + 0 ] = 2;           // 最大报文长度
  52.     rxtx_buffer[ TCP_OPTIONS_P + 1 ] = 4;           // TCP选项长度 TCP选项格式2
  53.     rxtx_buffer[ TCP_OPTIONS_P + 2 ] = HIGH(1408);  //
  54.     rxtx_buffer[ TCP_OPTIONS_P + 3 ] = LOW(1408);   //
  55.     // 数据偏移,占用高4位,且计算长度为双字
  56.     rxtx_buffer[ TCP_HEADER_LEN_P ] = 0x60;
  57.     dlength += 4;
  58.   }
  59.   else
  60.   {
  61.     // 没有TCP选项时长度为5个双字
  62.     rxtx_buffer[ TCP_HEADER_LEN_P ] = 0x50;
  63.   }

  64.   // generate ip header and checksum
  65.   ip_generate_header ( rxtx_buffer, (WORD_BYTES){(sizeof(IP_HEADER) + sizeof(TCP_HEADER) + dlength)}, IP_PROTO_TCP_V, dest_ip );

  66.   // 清除序号,一般使用于发送SYN时
  67.   if ( clear_seqack )
  68.   {
  69.     rxtx_buffer[ TCP_SEQACK_P + 0 ] = 0;
  70.     rxtx_buffer[ TCP_SEQACK_P + 1 ] = 0;
  71.     rxtx_buffer[ TCP_SEQACK_P + 2 ] = 0;
  72.     rxtx_buffer[ TCP_SEQACK_P + 3 ] = 0;
  73.   }
  74.    
  75.   // 设置TCP标志
  76.   rxtx_buffer [ TCP_FLAGS_P ] = flags;

  77.   // 设置目标端口号
  78.   rxtx_buffer [ TCP_DST_PORT_H_P ] = dest_port.byte.high;
  79.   rxtx_buffer [ TCP_DST_PORT_L_P ] = dest_port.byte.low;

  80.   // 设置源端口号
  81.   rxtx_buffer [ TCP_SRC_PORT_H_P ] = src_port.byte.high;
  82.   rxtx_buffer [ TCP_SRC_PORT_L_P ] = src_port.byte.low;

  83.   // 设置TCP窗口大小
  84.   rxtx_buffer [ TCP_WINDOWSIZE_H_P ] = HIGH((MAX_RX_BUFFER-sizeof(IP_HEADER)-sizeof(ETH_HEADER)));
  85.   rxtx_buffer [ TCP_WINDOWSIZE_L_P ] = LOW((MAX_RX_BUFFER-sizeof(IP_HEADER)-sizeof(ETH_HEADER)));

  86.   // 紧急指针
  87.   rxtx_buffer[ TCP_URGENT_PTR_H_P ] = 0;
  88.   rxtx_buffer[ TCP_URGENT_PTR_L_P ] = 0;

  89.   // 计算校验和
  90.   rxtx_buffer[ TCP_CHECKSUM_H_P ] = 0;
  91.   rxtx_buffer[ TCP_CHECKSUM_L_P ] = 0;
  92.   ck.word = software_checksum( &rxtx_buffer[IP_SRC_IP_P], sizeof(TCP_HEADER)+dlength+8, IP_PROTO_TCP_V + sizeof(TCP_HEADER) + dlength );
  93.   rxtx_buffer[ TCP_CHECKSUM_H_P ] = ck.byte.high;
  94.   rxtx_buffer[ TCP_CHECKSUM_L_P ] = ck.byte.low;

  95.   // 通过enc28j60发送数据
  96.   enc28j60_packet_send ( rxtx_buffer, sizeof(ETH_HEADER)+sizeof(IP_HEADER)+sizeof(TCP_HEADER)+dlength );
  97. }
复制代码
3.2 TCP负载长度查询
TCP负载长度查询需要根据IP报文中数据包的总大小和TCP报文中的数据偏移量决定,并需要注意TCP的数据偏移量的单位为双字,即4个字节长度。
  1. WORD tcp_get_dlength ( BYTE *rxtx_buffer )
  2. {
  3.   int dlength, hlength;
  4.   // 获得IP报文总大小
  5.   dlength = ( rxtx_buffer[ IP_TOTLEN_H_P ] <<8 ) | ( rxtx_buffer[ IP_TOTLEN_L_P ] );
  6.   // 除去IP报文头部大小
  7.   dlength -= sizeof(IP_HEADER);
  8.   // 获得TCP报文数据偏移量 单位为字,需要X4
  9.   hlength = (rxtx_buffer[ TCP_HEADER_LEN_P ]>>4) * 4;
  10.   // 除去TCP报文数据偏移量
  11.   dlength -= hlength;

  12.   if ( dlength <= 0 )
  13.     dlength=0;

  14.   return ((WORD)dlength);
  15. }
复制代码
3.3 TCP负载位置查询
  1. BYTE tcp_get_hlength ( BYTE *rxtx_buffer )
  2. {
  3.   // 获得TCP报文数据偏移量 单位为字,需要X4
  4.   return ((rxtx_buffer[ TCP_HEADER_LEN_P ]>>4) * 4);
  5. }
复制代码
3.4 TCP负载填充
       TCP的负载数据填充和UDP的负载数据填充相似。
  1. WORD tcp_puts_data_p ( BYTE *rxtx_buffer, PGM_P data, WORD offset )
  2. {
  3.   BYTE ch;

  4.   while( (ch = pgm_read_byte(data++)) )
  5.   {
  6.     rxtx_buffer[ TCP_DATA_P + offset ] = ch;
  7.     offset++;
  8.   }

  9.   return offset;
  10. }
  11. WORD tcp_puts_data ( BYTE *rxtx_buffer, BYTE *data, WORD offset )
  12. {
  13.   while( *data )
  14.   {
  15.     rxtx_buffer[ TCP_DATA_P + offset ] = *data++;
  16.     offset++;
  17.   }

  18.   return offset;
  19. }
复制代码
3.5 TCP数据包处理
       TCP数据包的处理代码较多,该函数会返回101代表以太网接收缓冲区的数据被处理,而0代表数据尚未被处理。TCP数据包的处理包含具体的应用实现,该部分出现在服务器端第二次返回ACK之后,具体的代码请结合范例和上文的TCP连接部分。
  1. BYTE tcp_receive ( BYTE *rxtx_buffer, BYTE *dest_mac, BYTE *dest_ip )
  2. {
  3.   WORD tcp_reclen, tcp_sendlen , dest_port;

  4.   // 获得目标端口号 即客户端端口号
  5.   dest_port = (rxtx_buffer[TCP_SRC_PORT_H_P]<<8)|rxtx_buffer[TCP_SRC_PORT_L_P];
  6.   // 匹配TCP协议类型,匹配端口
  7.   if ( rxtx_buffer [ IP_PROTO_P ] == IP_PROTO_TCP_V && \
  8.        rxtx_buffer [ TCP_DST_PORT_H_P ] == TCP_AVR_PORT_H_V && \
  9.        rxtx_buffer [ TCP_DST_PORT_L_P ] == TCP_AVR_PORT_L_V )
  10.   {
  11.     // 服务器端第1次发送 收到SYN 返回SYN+ACK
  12.     if ( (rxtx_buffer[ TCP_FLAGS_P ] & TCP_FLAG_SYN_V) )
  13.     {
  14.       tcp_send_packet (
  15.       rxtx_buffer,                          // 发送缓冲区
  16.       (WORD_BYTES){dest_port},              // 目标端口号
  17.       (WORD_BYTES){TCP_AVR_PORT_V},         // 源端口号
  18.       TCP_FLAG_SYN_V|TCP_FLAG_ACK_V,        // 标志位 同步位和应答位
  19.       1,                                    // 初始化序号,只有在接收到SYN时使用
  20.       0,                                    // 不清除确认号
  21.       1,                                    // 确认号为上一个数据包的应答编号加1,SYN占用一个序号
  22.       0,                                    // 负载长度
  23.       dest_mac,                             // 客户端MAC地址
  24.       dest_ip                               // 客户端IP地址
  25.       );

  26.       return 1;
  27.     }

  28.   // 收到ACK报文 多种情况
  29.   if ( (rxtx_buffer [ TCP_FLAGS_P ] & TCP_FLAG_ACK_V) )
  30.   {
  31.     // 获得TCP负载长度
  32.     tcp_reclen = tcp_get_dlength( rxtx_buffer );

  33.     if ( tcp_reclen == 0 )
  34.     {
  35.       // 服务器第4次发送,收到FIN,返回ACK
  36.       // 主要是为了区别 建立连接时的最后一个ACK
  37.       if ( (rxtx_buffer[TCP_FLAGS_P] & TCP_FLAG_FIN_V) )
  38.       {
  39.         tcp_send_packet (
  40.         rxtx_buffer,                    // 发送缓冲区
  41.         (WORD_BYTES){dest_port},        // 目标端口
  42.         (WORD_BYTES){TCP_AVR_PORT_V},   // 源端口
  43.         TCP_FLAG_ACK_V,                 // 标志位 应答
  44.         0,                              // 不操作序号
  45.         0,                              // 不清楚确认号
  46.         1,                              // FIN占一个序号,在一个数据包的序号的基础上累加1
  47.         0,                              // 负载大小
  48.         dest_mac,                       // 客户端MAC地址
  49.         dest_ip                         // 客户端IP地址
  50.         );
  51.       }

  52.       return 1;
  53.     }
  54.      
  55.     // 服务器第2次发送,返回ACK
  56.     tcp_send_packet (
  57.         rxtx_buffer,                    // 发送缓冲区
  58.         (WORD_BYTES){dest_port},        // 目标端口号
  59.         (WORD_BYTES){TCP_AVR_PORT_V},   // 源端口号
  60.         TCP_FLAG_ACK_V,                 // 标志位 应答标志
  61.         0,                              // 不操作序号
  62.         0,                              // 不清除确认号
  63.         tcp_reclen,                     // 在上一个数据包的基础上累加http_loadlen
  64.         0,                              // 负载长度
  65.         dest_mac,                       // 客户端MAC地址
  66.         dest_ip );                      // 客户端IP地址
  67.      
  68.     // TCP数据包数据,请查看后面的例子
  69.      
  70.     // 服务器第3次发送,发送HTTP响应 发送FIN
  71.     tcp_send_packet (
  72.             rxtx_buffer,                  // 发送缓冲区
  73.             (WORD_BYTES){dest_port},      // 目标端口
  74.             (WORD_BYTES){TCP_AVR_PORT_V}, // 源端口
  75.                                           // 标志
  76.             TCP_FLAG_ACK_V | TCP_FLAG_PSH_V | TCP_FLAG_FIN_V,   
  77.             0,                            // 不操作序号
  78.             0,                            // 不清除确认号
  79.             0,                            //
  80.             tcp_sendlen,                  // 负载长度
  81.             dest_mac,                     // 客户端MAC地址
  82.             dest_ip );                    // 客户端IP地址
  83.      // 数据包被处理
  84.      return 1;
  85.     }  
  86.   }

  87.   // 返回0 代表数据未被处理
  88.   return 0;
  89. }
复制代码
4.实验
       TCP报文的处理位于ARP IP ICMPICMP之后。获得TCP有效负载之后应存在在接收缓冲区中,进行合适的处理并返回结果。实验通过两个例子说明TCP的使用,第一个例子,PC机通过网络调试助手发送xukai871105AVR返回TCP Hello xukai871105。第二个例子,PC机发送lcd=Hello AVRNET,在AVRNETLCD显示屏上输出Hello AVRNET
4.1 程序结构
  1.   // 获得新的IP报文
  2.   plen = enc28j60_packet_receive( (BYTE*)&rxtx_buffer, MAX_RXTX_BUFFER );
  3.   if(plen==0) return;

  4.   // 保存客服端的MAC地址
  5.   memcpy ( (BYTE*)&client_mac, &rxtx_buffer[ ETH_SRC_MAC_P ], sizeof(MAC_ADDR) );
  6.   // 检查该报文是不是ARP报文
  7.   if ( arp_packet_is_arp( rxtx_buffer, (WORD_BYTES){ARP_OPCODE_REQUEST_V} ) )
  8.   {
  9.     // 向客户端返回ARP报文
  10.     arp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac );
  11.     return;
  12.   }

  13.   // 保存客服端的IP地址
  14.   memcpy ( (BYTE*)&client_ip, &rxtx_buffer[ IP_SRC_IP_P ], sizeof(IP_ADDR) );
  15.   // 检查该报文是否为IP报文
  16.   if ( ip_packet_is_ip ( (BYTE*)&rxtx_buffer ) == 0 )
  17.   {
  18.     return;
  19.   }

  20.   /* 如果是ICMP报文 向发起方返回数据 */
  21.   if ( icmp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac, (BYTE*)&client_ip ) )
  22.   {
  23.     return;
  24.   }

  25.   // 进行UDP处理
  26.        if (udp_receive ( (BYTE *)&rxtx_buffer, (BYTE *)&client_mac, (BYTE *)&client_ip ))
  27.        {
  28.      return;
  29.        }

  30.   // 进行TCP处理
  31.   if (tcp_receive ( (BYTE *)&rxtx_buffer, (BYTE *)&client_mac, (BYTE *)&client_ip ))
  32.   {
  33.     return;
  34.   }
复制代码
4.2 打印源IP和源端口4.3 TCP Hello
       TCP返回Hello的代码和UDP部分相似。在这里不多做解释。
  1.     // 确定TCP负载位置
  2.     WORD tcp_loadpos = tcp_get_hlength( rxtx_buffer) + sizeof(ETH_HEADER) + sizeof(IP_HEADER);
  3.    
  4.     // 复制缓冲区数据
  5.     memcpy(tcp_recbuf,(char*)&rxtx_buffer[tcp_loadpos],tcp_reclen);
  6.    
  7.     //准备返回数据
  8.     strcpy(tcp_sendbuf,"TCP:Hello ");
  9.     strcat(tcp_sendbuf,tcp_recbuf);
  10.     // 填充缓冲区
  11.     tcp_sendlen = tcp_puts_data( rxtx_buffer,(BYTE*)tcp_sendbuf,0);
复制代码
图 TCP Hello实验结果
4.4 LCD控制
       LCD控制命令和UDP中的LED控制命令的解析过程相似,从TCP输入负载中解析是否包含lcd字符串,若包含则从lcd=之后取出需要显示的字符串,接着清除lcd的所有显示,最后通过lcd打印输出结果。代码和实验结果如下。
  1.     tcp_recbuf[tcp_reclen] = '\0';
  2.     char lcd_buf[32];
  3.     if( !memcmp((char*)&tcp_recbuf,"lcd",3) )
  4.     {
  5.       strcpy(lcd_buf,(char*)&tcp_recbuf[4]);
  6.       // 清屏
  7.       lcd_putc ( '\f' );
  8.       // 通过lcd输出
  9.       lcd_print ( lcd_buf );
  10.       tcp_sendlen = tcp_puts_data( rxtx_buffer,(BYTE*)"lcd command",0);
  11.     }
  12.     else
  13.     {
  14.       tcp_sendlen = tcp_puts_data( rxtx_buffer,(BYTE*)"Unknow command",0);     
  15.     }
复制代码
LCD控制效果
5 总结和展望
       即使是一个不完整的TCP其实现过程也是非常复杂的。在web服务器的实现中,正是使用了本文所用到的TCP处理结构,也就是说TCPHTTP的基础。接着将实现AVRNET项目中最令人兴奋的web服务器功能,并将通过单个静态网页,单个动态网页和多个动态网页实现嵌入式WEB服务器。


web服务器实现LED控制


工程源码,请各位笑纳,如果觉得程序有任何问题,请毫不客气的提出!


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-2-16 00:25:49 | 显示全部楼层
楼主最近发的这几篇帖子都很好啊~~~收藏了~~~

出0入0汤圆

发表于 2013-2-16 01:12:43 | 显示全部楼层
mark         

出0入0汤圆

发表于 2013-2-16 03:49:01 来自手机 | 显示全部楼层
深夜无法睡眠,顶贴。

出0入0汤圆

发表于 2013-2-16 05:47:30 | 显示全部楼层
记号,备用

出0入0汤圆

发表于 2013-2-16 07:01:37 | 显示全部楼层
學習,謝謝.

出0入0汤圆

发表于 2013-2-16 08:38:25 | 显示全部楼层
不知道LZ用这试过垮网没

出0入0汤圆

 楼主| 发表于 2013-2-16 09:42:54 | 显示全部楼层
xad74 发表于 2013-2-16 08:38
不知道LZ用这试过垮网没

跨网最好有以下前提条件
1.AVR一侧最好有电信级的固定IP地址,在主路由器中做端口映射,例如把192.168.1.105,映射成电信级IP地址的端口3002,这称之为静态映射。
2.若没有电信级IP地址,可通过花生壳代理。第一,设置路由器中的动态DNS,设定服务器提供者,账户名和密码。第二,设置DHCP,将AVR的IP地址和MAC地址绑定。第三,设定虚拟服务器,把广域服务器的端口设定为AVR的IP地址。

技术要点,此时以发送和接收数据包是,以太网数据的MAC地址和IP首部中的IP地址将没有对应关系,以太网首部中的MAC地址将会是路由器的MAC地址。

出0入0汤圆

发表于 2013-2-16 09:48:03 | 显示全部楼层
设备在内网设置成Client也可以跨网段,往外连主机,这样比较安全。

出0入0汤圆

发表于 2013-2-17 11:09:29 | 显示全部楼层
顶一下。。好资料

出0入0汤圆

 楼主| 发表于 2013-2-17 12:38:12 | 显示全部楼层
dabi 发表于 2013-2-17 11:09
顶一下。。好资料

本还有WEB部分的内容,但是毕竟是仿真,始终存在某些问题!
我把这些内容重新再STM32上测试了一下,也可以使用了!

虽然写这些材料很费时间,但是对于我个人而言,以太网的各部分算是熟悉了
回过头来看uIP和lwIP的话就简单多了!在这些平台的基础上做web或是其他内容就清楚的多了!

出0入0汤圆

发表于 2013-2-17 14:18:47 | 显示全部楼层
本帖最后由 xad74 于 2013-2-17 14:27 编辑

没有全部看完LZ的代码,但我觉得跟“AVRNET项目(国外)”的TCP/IP协议差不多。前阵也用它的协议做过,在公司的网络(192和1722个网段)内没法跑通,后又在同网段不同VLAN三层交换机上也没跑通。
个人觉得好像是他的ARP上有问题,它没有ARP表只有一个远端IP地址。
后又加入了网关但故障依旧
始终搞不懂数据时如何通过路由或三层交换机进行交换的。从网上搜到的资料说当IP地址加子网掩码之和如和远端地址加子网掩码之和不等那就认为不在一网段内,需通过网关来跑数据。路由呢好像不是通过网关走的,但路由又没法发广播数据。
再后来换了个协议现在192和172的网段间能走通数据了,在调试时看了下新协议中的ARP,当数据通时多是2个IP地址和2个MAC地址
传上网上的资料

假设有两个使用IP协议的站点,通过第三层交换机进行通信的过程为:若发送站点A在开始发送时,已知目的站B的IP地址,但尚不知道它在局域网上发送所需要的MAC 地址,则需要采用地址解析(ARP)来确定B的MAC 地址。A把自己的IP 地址与B的IP 地址比较,采用其软件中配置的子网掩码提取出网络地址来确定B是否与自己在同一子网内。若B 与A 在同一子网内,A 广播一个ARP 请求,B 返回其MAC 地址,A 得到B 的MAC 地址后将这一地址缓存起来,并用此MAC 地址封包转发数据,第二层交换模块查找MAC 地址表确定将数据包发向目的端口。若两个站点不在同一子网内,则A 要向"缺省网关"发出ARP(地址解析)封包,而"缺省网关"的IP 地址已经在系统软件中设置,这个IP 地址实际上对应第三层交换机的第三层交换模块。当A 对"缺省网关"的IP 地址广播出一个ARP 请求时,若第三层交换模块在以往的通信过程中已得到B 的MAC 地址,则向发送站A 回复B 的MAC 地址;否则第三层交换模块根据路由信息向目的站广播一个ARP 请求,B 得到此ARP 请求后向第三层交换模块回复其MAC 地址,第三层交换模块保存此地址并回复给发送站A .以后,当再进行A 与B 之间数据包转发时,将用最终的目的站点的MAC 地址封包,数据转发过程全部交给第二层交换处理,信息得以高速交换2.3


以上是我个人的一些认识,因对路由和三层交换机实在不怎么了解。希望坛子里对这方面有所了解的大虾能好好说说

出0入0汤圆

发表于 2013-2-17 16:37:56 | 显示全部楼层
你的仿真ping不通AVR地址

出0入0汤圆

 楼主| 发表于 2013-2-17 18:55:35 | 显示全部楼层
本帖最后由 xukai871105 于 2013-2-17 19:15 编辑
lnso 发表于 2013-2-17 16:37
你的仿真ping不通AVR地址


如果是仿真的话
第一,先检查IP地址是不是在一个子网中!(PC机和IP地址)
第二,打开ARP.c中的ARP_DEBUG,定义为1,重新编译,查看是否有ARP请求。如果没有请尝试第三!
第二,请使用有线网卡,如果是无线网卡的话的确是没有办法仿真!
第三,使用的Wireshark分析网络数据!

出0入0汤圆

 楼主| 发表于 2013-2-17 19:14:54 | 显示全部楼层
xad74 发表于 2013-2-17 14:18
没有全部看完LZ的代码,但我觉得跟“AVRNET项目(国外)”的TCP/IP协议差不多。前阵也用它的协议做过,在公司 ...

在公司上班的时候查看到你的回帖!由于网管还在旅游途中,我无法在公司中实践一下!

你的描述应该是正确的,如果发现目标IP地址和源IP地址不在同一个子网中的话,应向路由器发送该报文!
此时 以太网首部中的目标MAC地址为路由器IP地址,IP首部中的目标地址为B地址。

我通过wireshark分析是这样的结果!ping www.baidu.com
其中以太网首部中为TPLINK的MAC地址
IP首部为百度的美国服务器

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-2-22 20:02:38 | 显示全部楼层
支持楼主!

出0入0汤圆

发表于 2013-2-22 23:38:31 来自手机 | 显示全部楼层
表示支持!

出0入0汤圆

发表于 2013-3-13 08:54:55 | 显示全部楼层
最近半月一直在读楼主的帖子,对着楼主推荐的书看,收获颇丰
帖子内容给详细易懂,初学者都应该好好看看

出0入0汤圆

 楼主| 发表于 2013-3-13 09:24:28 | 显示全部楼层
shuiyunzhijian 发表于 2013-3-13 08:54
最近半月一直在读楼主的帖子,对着楼主推荐的书看,收获颇丰
帖子内容给详细易懂,初学者都应该好好看看 ...

我在整理一下HTTP的吧,仿真是做不下去了!
我把他修改到STM32上去!

出0入0汤圆

发表于 2013-3-17 09:15:17 | 显示全部楼层
昨天看代码(http部分),遇到问题如下,请教楼主:
        // get data position
        data_p = tcp_get_hlength( rxtx_buffer )+ sizeof(ETH_HEADER) + sizeof(IP_HEADER);
这个sizeof(ETH_HEADER)和sizeof(IP_HEADER)的值,都是2,而不是分别为14和20.
楼主试试 DDRA=sizeof(IP_HEADER); 然后studio软件调试看看DDRA的值。
不知道为什么这个情况
我是新手,问题较菜,希望楼主不吝解答。

出0入0汤圆

 楼主| 发表于 2013-3-17 15:11:42 | 显示全部楼层
shuiyunzhijian 发表于 2013-3-17 09:15
昨天看代码(http部分),遇到问题如下,请教楼主:
        // get data position
        data_p = tcp_get_hlength( rx ...

这个问题也困扰了我很久!
sizeof的运算结果。IAR和AVR Studio 6的始终不同!没有办法,也找不到原因!

实在无奈,我把所有使用sizeof的地方,换成了宏定义,例如xxx_headlen!这才在IAR中实现了!

出0入0汤圆

发表于 2013-3-17 22:46:56 | 显示全部楼层
xukai871105 发表于 2013-3-17 15:11
这个问题也困扰了我很久!
sizeof的运算结果。IAR和AVR Studio 6的始终不同!没有办法,也找不到原因!

哦,这个样子啊。
同样感谢楼主。

出0入296汤圆

发表于 2013-3-18 00:17:01 | 显示全部楼层
这个必须要支持的。难得的高质高量的技术交流~

出0入0汤圆

发表于 2013-3-18 14:33:52 | 显示全部楼层
支持一下,有空好好看看

出0入0汤圆

 楼主| 发表于 2013-3-18 19:46:49 | 显示全部楼层
Gorgon_Meducer 发表于 2013-3-18 00:17
这个必须要支持的。难得的高质高量的技术交流~

非常感谢你的回复,多向你学习,我一定继续努力!

出0入0汤圆

发表于 2013-3-22 14:46:07 | 显示全部楼层
感谢楼主,讲的太好了!

出0入0汤圆

 楼主| 发表于 2013-3-23 14:15:13 | 显示全部楼层
犯戒和尚 发表于 2013-3-22 14:46
感谢楼主,讲的太好了!

多谢支持!

出0入0汤圆

发表于 2013-4-9 21:40:32 | 显示全部楼层
xukai871105 发表于 2013-3-23 14:15
多谢支持!

“即使是一个不完整的TCP其实现过程也是非常复杂的。在web服务器的实现中,正是使用了本文所用到的TCP处理结构,也就是说TCP是HTTP的基础。接着将实现AVRNET项目中最令人兴奋的web服务器功能,并将通过单个静态网页,单个动态网页和多个动态网页实现嵌入式WEB服务器。”

楼主啥时候分享一下这部分内容?如果我想掌握这部分,需要先去掌握什么知识?

出0入0汤圆

 楼主| 发表于 2013-4-10 10:58:50 | 显示全部楼层
NIC 发表于 2013-4-9 21:40
“即使是一个不完整的TCP其实现过程也是非常复杂的。在web服务器的实现中,正是使用了本文所用到的TCP处 ...

最近上班了,在学习MSP430,但是以前没有使用过!
如果是WEB方面的内容的话,说实话学习的时间会很长!
需要花点时间学习一下HTML PHP,基础知识还是比较重要的

出0入0汤圆

发表于 2013-4-10 11:36:08 | 显示全部楼层
谢谢。留着备用学习

出0入0汤圆

发表于 2013-4-10 11:50:59 | 显示全部楼层
多谢楼主分享

出0入0汤圆

发表于 2013-4-10 12:45:28 | 显示全部楼层
xukai871105 发表于 2013-4-10 10:58
最近上班了,在学习MSP430,但是以前没有使用过!
如果是WEB方面的内容的话,说实话学习的时间会很长!
...

那如果开发那种客户端平台软件需要掌握哪些方面知识?

出0入0汤圆

发表于 2013-4-10 14:54:13 | 显示全部楼层
留个脚印,以后肯定能用得上、、

出0入0汤圆

 楼主| 发表于 2013-4-10 17:27:36 | 显示全部楼层
NIC 发表于 2013-4-10 12:45
那如果开发那种客户端平台软件需要掌握哪些方面知识?

要我的话我选择C#,可以使用HTTP通信,也可以使用TCP UDP通信。
IDE我会选择SharpDevelop,VS2010/2012实在是太大了!

出0入0汤圆

发表于 2013-4-14 10:26:19 | 显示全部楼层
tcp_send_packet ()函数的第61行,dlength += 4;
我了解到“最大报文段长度”字段长4字节(1个字)
请问这4字节算TCP头,还是算数据?
如果看做TCP头,那为何dlength += 4
如果看做数据,那为何rxtx_buffer[ TCP_HEADER_LEN_P ] = 0x60而不是0x50?
请教楼主,非常感谢。

出0入0汤圆

发表于 2013-4-14 20:52:17 | 显示全部楼层
正在学习TCP/IP,谢谢楼主

出0入0汤圆

发表于 2013-6-12 17:22:43 | 显示全部楼层
谢谢楼主分享

出0入4汤圆

发表于 2013-6-12 18:43:11 | 显示全部楼层
这个可以有!

出0入0汤圆

发表于 2013-10-5 22:45:57 | 显示全部楼层
明天试下TCP

出0入0汤圆

发表于 2013-10-6 20:02:29 | 显示全部楼层
TCP真的要复杂多啦

出0入0汤圆

发表于 2013-10-12 20:04:16 | 显示全部楼层
先收藏了以后学习,谢谢楼主分享!

出0入0汤圆

发表于 2013-11-26 06:43:38 | 显示全部楼层
很好,正学习了

出0入0汤圆

发表于 2013-12-18 13:05:26 | 显示全部楼层
学习一下                              

出0入0汤圆

发表于 2013-12-18 14:46:13 | 显示全部楼层
好,学习了

出0入0汤圆

发表于 2013-12-20 18:40:09 | 显示全部楼层
mark a mark

出0入0汤圆

发表于 2013-12-20 19:04:54 | 显示全部楼层
markAVR NET

出0入0汤圆

发表于 2014-1-10 10:25:07 | 显示全部楼层
mark,努力学习中

出0入0汤圆

发表于 2014-9-2 07:12:28 来自手机 | 显示全部楼层
不错,mark一下。

出0入0汤圆

 楼主| 发表于 2014-9-2 21:40:16 | 显示全部楼层
dqjhf 发表于 2014-9-2 07:12
不错,mark一下。

http://blog.csdn.net/xukai871105 mark这里吧

出0入0汤圆

发表于 2015-1-9 10:00:25 | 显示全部楼层
楼主提供了一个很好的自学思路,打算按照这个思路开始学习嵌入式以太网。

出0入0汤圆

 楼主| 发表于 2015-1-9 17:04:29 | 显示全部楼层
zrj2015 发表于 2015-1-9 10:00
楼主提供了一个很好的自学思路,打算按照这个思路开始学习嵌入式以太网。 ...

我照的这个做了很多年,收获颇丰

出0入0汤圆

发表于 2015-1-9 17:29:32 | 显示全部楼层
收藏,楼主是网络大师啊

出0入0汤圆

发表于 2015-1-9 17:30:19 | 显示全部楼层
xukai871105 发表于 2014-9-2 21:40
http://blog.csdn.net/xukai871105 mark这里吧

趁机也关注一下

出0入0汤圆

 楼主| 发表于 2015-1-9 20:47:28 | 显示全部楼层
tanek 发表于 2015-1-9 17:29
收藏,楼主是网络大师啊

网络初学者,大师我还早呢。

出0入0汤圆

发表于 2015-1-9 23:57:56 | 显示全部楼层
   收藏了,楼主资料不错

出0入0汤圆

发表于 2015-1-11 12:56:48 | 显示全部楼层
看了楼主的帖子,受益匪浅啊!

出0入0汤圆

 楼主| 发表于 2015-1-12 20:14:44 | 显示全部楼层
zyin123 发表于 2015-1-11 12:56
看了楼主的帖子,受益匪浅啊!

去博客看看吧,资料还多点!

出0入0汤圆

发表于 2015-1-12 20:35:28 | 显示全部楼层
   谢谢你的无私分享.

我最近也打算做嵌入式以太网相关的课题,以前只肤浅的了解了一些TCP/IP协议的知识,对实在的做,还真没有把握呢。

出0入0汤圆

 楼主| 发表于 2015-1-13 20:24:48 | 显示全部楼层
daishixin 发表于 2015-1-12 20:35
谢谢你的无私分享.

我最近也打算做嵌入式以太网相关的课题,以前只肤浅的了解了一些TCP/IP协 ...

嵌入式TCP IP其实不难,花点时间理解一下,一定会有收获的。

出0入0汤圆

发表于 2015-1-15 19:59:23 | 显示全部楼层
楼主 AVR高手啊 学习了

出0入0汤圆

发表于 2015-1-15 20:04:13 来自手机 | 显示全部楼层
楼主出的几个贴都不错

出0入0汤圆

 楼主| 发表于 2015-1-16 09:31:13 | 显示全部楼层
qqwwrm110 发表于 2015-1-15 19:59
楼主 AVR高手啊 学习了

到我的博客看看吧,我可不是AVR高手。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-10 22:07

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表