专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

ARM7单片机+RTL8019嵌入式TCP_IP协议栈使用分析

作者:杨文斌   来源:本站原创   点击数:  更新时间:2008年10月01日   【字体:

杨文斌
2008-9-1
一、 总则
本文件是嵌入式TCP/IP协议栈的说明文件,嵌入式TCP/IP应用开发人员可通过阅读本文件,掌握在嵌入式TCP/IP协议栈的基础上开发服务器和客户端应用程序,如FTP服务器,WEB服务器,串口服务器等等。
二、 参考文件
1) TCP_IP详解卷1,2,3
2) RFC 959 (rfc959) - File Transfer Protocol.htm
3) rfc1945- Hypertext Transfer Protocol -- HTTP/1.0
三、 技术说明
1) 用户应用协议栈则需要编写以太网的数据报收发驱动,就可以使用协议栈提供的标准SOCKET API,完成服务器和客户端应用程序的开发。
2) 协议栈运行于非操作系统的环境下,因此它的运行速度与一般采用多任务操作系统的TCP/IP协议,速度相对说来要快。
3) 协议栈完成的功能包括ARP,IP,ICMP(ping),TCP,UDP,暂不支持IGMP,RARP。
4) 协议栈采用C代码编写,可方便的移植于各种单片机平台。
5) 协议栈在ARM7+RTL8019硬件环境下测试,并建立了FTP服务器和WEB服务器,性能稳定。
6) 协议栈建立的FTP服务器和WEB服务器与Internet Explorer浏览器和ftp.exe相互兼容。
7) 协议栈每一个SOCKET上建了数据缓冲队列(数据结构),用于接收SOCKET的并发数据,实现多SOCKET的并发数据报处理,可同时运行FTP服务器和WEB服务器。
8) 协议栈实现了ACK的延时答应(200ms),支持TCP多包发送和接收,但未支持TCP数据报的失序处理,因此适合局域网内使用。

四、 SOCKET API函数
1) 函数SOCKET * socket(u16 af,u16 type,u16 protocol)
本函数功能是从SOCKET pool中分配一个SOCKET插口,供应用程序使用,其参数说明如下:
1. 参数af,type—无意义,保留为扩充功能使用。
2. 参数protocol—为分配SOCKET的类型,包括TCP_PROTOCOL和UDP_PROTOCOL两个类型。
3. 返回值:函数执行成功,返回SOCKET*指针指向一个SOCKET,失败返回NULL
2) 函数u16 bind(SOCKET * sock,struct sockaddr * address,u8 len)
本函数功能是将IP地址和端口绑定到一个SOCKET 指针* sock指向的SOCKET。
1. SOCKET * sock—指向被绑定的SOCKET。
2. struct sockaddr * address—指向IP地址和端口。
3. len—无意义,保留为扩充功能使用。
4. 返回值:SUCC。
3) 函数u16 listen(SOCKET * sock, u16 QTY)
本函数功能是启动被绑定了地址和端口的 SOCKET * sock,触发其为监听状态。本函数由服务器端应用程序使用。
1. SOCKET * sock—指向被bind的SOCKET。
2. 返回值:SUCC。
4) 函数u16 connect(SOCKET * sock, struct sockaddr * sevaddr,u8 len)
本函数功能是用于建立一个连接,到服务器,服务器的地址和端口由参数sevaddr指定。该函数由客户端使用。
1. SOCKET * sock—指向被连接的本地SOCKET。
2. struct sockaddr * sevaddr,-- 服务器的地址和端口.
3. u8 len—无意义,保留为扩充功能使用。
4. 返回值:是SOCKET句柄。
5) 函数u16 accept(SOCKET * sock,struct sockaddr * address,u16 *iii)
本函数返回一个已连接的SOCKET句柄,供函数recv(),send()收发数据使用。
1. SOCKET * sock—指向一个被绑定地址和端口的SOCKET
2. struct sockaddr * address,u16 *iii--无意义,保留为扩充功能使用。
3. 返回值:是SOCKET句柄。
6) 函数u16 recv(u16 handle,u8 * rec_buff,u16 len,u16 i)
指定句柄读取数据,由TCP使用
1. u16 handle--指定句柄
2. u8 * rec_buff—缓冲区首地址
3. u16 len—读取数据的长度
4. u16 i--无意义,保留为扩充功能使用。
5. 返回值:为已读取的字节数
7) 函数u8 send(u16 handle,u8 *rec_buff,u16 len,u16 i)
向指定句柄发送数据,由TCP使用
1. u16 handle--指定句柄
2. u8 * rec_buff—缓冲区首地址
3. u16 len—发送数据的长度 
4. u16 i--无意义,保留为扩充功能使用。
5. 返回值:是SUCC
8) void close(u16 handle)
发送FIN主动关闭一个SOCKET连接,handle为被关闭连接的句柄。

9) 函数u16 recvfrom(SOCKET *sock,u8 *rec_buff,u16 len,u8 i,struct sockaddr * address,u16 *addr_len)
从指定SOCKET *sock插口读取数据,由UDP使用,函数的参数具体情况如下:
1. SOCKET *sock --指向插口的指针
2. u8 * rec_buff—缓冲区首地址
3. u16 len—读取数据的长度
4. u16 i--无意义,保留为扩充功能使用。
5. 返回值:为已读取的字节数

10) 函数u16 sendto(SOCKET *sock,u8 *rec_buff,u16 len,u8 i,struct sockaddr  * address,u16 *addr_len)
向从指定SOCKET *sock插口发送数据,由UDP使用,函数的参数具体情况如下:
1. SOCKET *sock,--指向插口的指针
2. u8 * rec_buff—缓冲区首地址
3. u16 len—发送数据的长度
4. u16 i--无意义,保留为扩充功能使用
5. 返回值:为SUCC

五、 SOCKET API应用举例
1) 简单WEB服务器--通过函数TCP_TEST()完成设置本地TCP服备器的IP地址,其过程如下:
1. 调用SOCKET API函数socket(0,0,TCP_PROTOCOL)分配一个SOCKET,
2. 调用SOCKET API函数将TCP server的IP地址与SOCKET绑定,调用函数bind()起动监听。
3. 函数TCP_TEST()通过函数accept()接收网页获取请求,调用函数recv()接收HTTP命令,根据命令调用函数send()发送http网页。
WEB服务器程序清单
/*****************************************************
* 名称:TCP_TEST()
* 功能:设置TCP模块
* 入口:无
* 出口: 无
****************************************************************************/ 
 
void TCP_SETUP(void)
{
  /*设置本地TCP服备器的IP地址*/
  TCP_serveraddr.sin_family = 0;
  TCP_serveraddr.sin_addr[0] = MY_IP_ADD[0];
  TCP_serveraddr.sin_addr[1] = MY_IP_ADD[1];
  TCP_serveraddr.sin_addr[2] = MY_IP_ADD[2];
  TCP_serveraddr.sin_addr[3] = MY_IP_ADD[3];
  TCP_serveraddr.sin_port = 80;
  /*将TCP server的IP地址与SOCKET绑定*/
  t = socket(0,0,TCP_PROTOCOL);
  iii=bind(t,&TCP_serveraddr,sizeof(TCP_serveraddr));
  iii=listen(t,4);
 
}
/****************************************************************************
* 名称:TCP_TEST()
* 功能:TCP打开网页测试
* 入口:无
* 出口: 无
****************************************************************************/
void TCP_TEST(void)
{
  temp = accept(t,&TCP_clientaddr,&iii);/*accept网页获取请求*/
 
  if(temp != 0xffff)
  {  templen = recv(temp,TCP_rec_buff,1024,0);
     if(TCP_rec_buff[5] == ' ')
     {       send(temp,httpweb,169,0);  /*发送http网页*/
       send(temp,web,395,0);
      
     }
     else if(TCP_rec_buff[5] == '1')
     {
       send(temp,httpgif,169,0);  /*发送GIF,BMP图片背景*/
       send(temp,bmp,442,0);    
     }
     close(temp);    
  }
}

2) 简单UDP服务器—通过函数UDP_TEST()完成设置本地UDP服备器的IP地址和远端口服务器的IP地址, 其过程如下:
1. 调用SOCKET API函数socket(0,0,TCP_PROTOCOL)分配一个SOCKET
2. 调用SOCKET API函数bind()将UDP server的IP地址与SOCKET绑定,将调用SOCKET API函数enable_a_port_listen(1025)起动监听。
3. 函数UDP_TEST()通过函数recfrom()接收UDP数据报,接收到的UDP数据报调用SOCKET API函数sendto()回传远程服务器。
UDP服务器程序清单
/****************************************************************************
* 名称:UDP_SETUP()
* 功能:设置UDP模块
* 入口:无
* 出口: 无
****************************************************************************/
void UDP_SETUP(void)
{
  serveraddr.sin_family = 0;   /*设置远端服务器的IP地址*/
  serveraddr.sin_addr[0] = 192;
  serveraddr.sin_addr[1] = 168;
  serveraddr.sin_addr[2] = 0;
  serveraddr.sin_addr[3] = 1;
  serveraddr.sin_port = 1026;
  s = socket(0,0,UDP_PROTOCOL);
  clientaddr.sin_family = 0;  /*设置本地UDP客户端的IP地址*/
  clientaddr.sin_addr[0] = MY_IP_ADD[0];
  clientaddr.sin_addr[1] = MY_IP_ADD[1];
  clientaddr.sin_addr[2] = MY_IP_ADD[2];
  clientaddr.sin_addr[3] = MY_IP_ADD[3];
  clientaddr.sin_port = 1025;
  /*将本地IP地址与SOCKET绑定*/
  iii=bind(s,&clientaddr,sizeof(clientaddr));
  enable_a_port_listen(1025);
}  

 /****************************************************************************
* 名称:UDP_TEST()
* 功能:UCP数据报收发测试
* 入口:无
* 出口: 无
****************************************************************************/ 
void UDP_TEST(void)

  /*接收UDP数据报*/
  len = recvfrom(s,rec_buff,400,0,&serveraddr,&iii);
  if(len > 0) 
    { /*将接收到的UDP数据报发送回服务器端*/
      sendto(s,rec_buff,len,0,&serveraddr,&iii);
    }
}
 

关闭窗口