嵌入式TCP_IP协议栈说明书杨文斌2008-9-1一、总则本文件是嵌入式TCP/IP协议栈的说明文件,嵌入式TCP/IP应用开发人员可通过阅读本文件,掌握在嵌入式TCP/IP协议栈的基础上开发服务器和客户端应用程序,如FTP服务器,WEB服务器,串口服务器等等。
二、参考文件1)TCP_IP详解卷1,2,32)RFC 959 (rfc959) - File Transfer Protocol.htm3)rfc1945- Hypertext Transfer Protocol -- HTTP/1.0三、技术说明1)用户应用协议栈则需要编写以太网的数据报收发驱动,就可以使用协议栈提供的标准SOCKETAPI,完成服务器和客户端应用程序的开发。
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,失败返回NULL2)函数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—指向一个被绑定地址和端口的SOCKET2.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.返回值:是SUCC8)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)分配一个SOCKET2.调用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);}}。