RL-TCPnet之TCP服务器(enc28j60)

参考安富莱的教程<安富莱_STM32-V5开发板_RL-TCPnet网络教程(V1.0)>

  上节的RL-TCPnet移植 


一、’Socket和监听的关系

 创建的一个socket只能创建一个监听。

  创建的一个socket不能够监听多个 。

  创建多个socket可以创建多个监听。 

 创建多个socket可以仅创建一个监听。

二、API函数介绍

1、函数tcp_get_socket

U8 tcp_get_socket (
    U8   type,          /* Type of TCP socket。 */  TCP Socket类型
    U8   tos,           /* Type Of Service。 */    TCP服务类型
    U16  tout,          /* Idle timeout period before disconnecting。 */ 断开连接前的空闲溢出时间 ()
    U16 (*listener)(    /* Function to call when a TCP event occurs。 */ 回调函数
    U8  socket,     /* Socket handle of the local machine。 */Socket句柄
    U8  event,      /* TCP event such as connect, or close。 */ TCP事件
    U8* ptr,        /* Pointer to IP address of remote machine, or pointer to buffer containing received data。 */记录接收 到的数据或者 远程 机器的 IP
    U16 par ));     /* Port number of remote machine, or length of received data。 */ 记录 接收到的数据长度或者远程机器端口号 

第1个参数是TCP Socket的类型。 
Socket类型                 描述 
TCP_TYPE_SERVER            TCP服务器。
TCP_TYPE_CLIENT            TCP客户端。
TCP_TYPE_CLIENT_SERVER     同时支持TCP客户端和TCP服务器。
TCP_TYPE_DELAY_ACK         延迟应答,通过此属性可以提高发送大批量数据的性能,比如http server。此属性可以通过或操作(|)与其它属性一起使用。 
TCP_TYPE_FLOW_CTRL         TCP流控制。此属性可以通过或操作(|)与其它属性一起使用。
TCP_TYPE_KEEP_ALIVE        当溢出时间到,TCP Socket会自动发送一个keep-alive数据包来保持连接。此属性可以通过或操作(|)与其它属性一起使用。 
第2个参数用于指定服务类型,默认取零即可。
第3个参数用于设置空闲溢出时间,单位秒。Keep alive定时器用于监控TCP连接,如果连接的空闲时间(也就是长时间没有数据通信)超出了,那么会断开连接。如果设置了TCP_TYPE_KEEP_ALIVE属性,会通过发送keep alive数据包来保持连接
第4个参数是回调函数,用于事件监听
回调函数第1个参数,TCP Socket的句柄,也就是函数tcp_get_socket的返回值。
回调函数第2个参数,事件类型。
回调函数第3个参数,事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址。
回调函数第4个参数,记录接收到的数据个数,其余事件记录端口号
返回值,如果获取成功,返回TCP Socket句柄,如果获取失败,返回0。


2、函数tcp_listen

BOOL tcp_listen (
  U8socket,       /* TCP socket to listen with。 */ TCP socket句柄
  U16 locport );  /* TCP port number to listen at。 */  监听端口号
函数tcp_listen用于设置TCP服务器的监听端口。 

 第1个参数是要设置监听的TCP Socket句柄。 第2个参数是监听端口号。 

 返回值,创建监听成功返回__TRUE,创建失败返回__FALSE。


3、函数tcp_check_send

BOOL tcp_check_send (
    U8 socket );    /* TCP socket to check whether it can send data。 *TCP socket句柄
函数tcp_check_send用于检测是否可以发送数据。此函数通过检测TCP连接是否建立以及上次发送的数据是否接收到远程机器的应答来判断是否可以发送数据。

第1个参数是TCP Socket句柄。 

 返回值,可以发送数据,返回__TRUE;不可以发送数据,返回__FALSE。


4、函数tcp_max_dsize

U16 tcp_max_dsize (
  U8 socket );  /* TCP socket to get the Maximum Segment Size of。 */ TCP socket句柄
函数tcp_max_dsize用于获得当前可以发送的最大报文长度(MSS,Maximum Segment Size)。在配置向导中,默认配置的MSS是1460字节,然而在实际建立连接后,此值会被动态调整,但一定是小于等于1460字节的。 

第1个参数是TCP Socket句柄。 

 返回值,返回本次可以发送的最大报文长度,单位字节。


5、函数tcp_get_buf

U8* tcp_get_buf (
  U16 size );  /* Number of bytes to be sent。 */
函数tcp_get_buf用于获取TCP发送缓冲区,用户将要发送的数据存到这个缓冲区中,然后通过函数tcp_send发送。发送完毕后要等待远程主机的应答,收到应答后,会在函数tcp_send中释放申请的发送缓冲区。 

 第1个参数是要申请的缓冲区大小。 

 返回值,返回获取的缓冲区地址


6、函数tcp_send

BOOL tcp_send (
  U8socket,  /* TCP socket to send the data packet from。 */  TCP socket句柄
  U8* buf,       /* Pointer to buffer containing the data to send。 */发送数据地址
  U16 dlen );  /* Number of bytes of data to send。 */             发送数据字节数
函数tcp_send用于数据包发送。 
第1个参数是TCP Socket句柄。  
第2个参数是函数tcp_get_buf获取的缓冲区地址。 
第3个参数是发送数据个数,单位字节。 
返回值,发送成功返回__TRUE,发送失败返回__FALSE。

7、函数tcp_get_state

U8 tcp_get_state (
    U8 socket );    /* TCP socket to get the state of。 */ TCP socket句柄
函数tcp_get_state用于获取TCP Socket的当前状态。用户应用程序可以通过此函数监控TCP Socket的连接、断开等状态。最有用的状态值是TCP_STATE_CLOSED,TCP_STATE_LISTEN和TCP_STATE_CONNECT。

状态

描述

TCP_STATE_FREE

Socket空闲没有被分配。函数不能返回这个值。

TCP_STATE_CLOSED

Socket分配给一个应用程序但是连接是关闭的。

TCP_STATE_LISTEN

Socket监听输入连接。

TCP_STATE_SYN_REC

Socket收到带SYN标志TCP包。

TCP_STATE_SYN_SENT

Socket发送带SYN标志TCP包。

TCP_STATE_FINW1

Socket发送FIN包,以关闭连接。

TCP_STATE_FINW2

Socket接收到远程机器对FIN包的确认,它来自本地机器的发送。Socket现在等待来自远程机器的FIN包。

TCP_STATE_CLOSING

Socket独立地接收到来自远程机器的FIN包。

TCP_STATE_LAST_ACK

Socket等待它发出的给到FIN包的最后ACK包。

TCP_STATE_TWAIT

Socket在关闭连接之前等待2 ms 时间。

TCP_STATE_CONNECT

Socket建立了TCP连接,只有在这一状态下才能传送数据。


三、代码实现


uint8_t socket_tcp = 0;
uint8_t RecData1[1500] = {1,2,3,4,4,5};//接收到数据
uint8_t RecLen1 = 0;         //接收长度


#define PORT_NUM       1001    /* TCP服务器监听端口号 */
#if 1
#define printf_debug printf
#else
#define printf_debug(...)
#endif


/*
*********************************************************************************************************
* 函 数 名: tcpnet_poll
* 功能说明: 使用TCPnet必须要一直调用的函数
* 形    参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void tcpnet_poll1(void)
{
if(bsp_CheckTimer(0))
{
/* 此函数坚决不可以放在中断里面跑 */
timer_tick ();

bsp_LedToggle(1);
}

main_TcpNet ();
}


/*
*********************************************************************************************************
* 函 数 名: TCP_StatusCheck
* 功能说明: 检测TCP的连接状态,主要用于网线插拔的判断
* 形    参: 无
* 返 回 值: __TRUE  连接
*             __FALSE 断开
*********************************************************************************************************
*/
uint8_t TCP_StatusCheck(void) 
{
uint8_t res;

switch (tcp_get_state(socket_tcp)) 
{
case TCP_STATE_FREE:
case TCP_STATE_CLOSED:
res = tcp_listen (socket_tcp, PORT_NUM);
printf_debug("tcp listen res = %d\r\n", res);
break;

case TCP_STATE_LISTEN:
break;

case TCP_STATE_CONNECT:
return (__TRUE);

default:  
break;
}

return (__FALSE);
}




/*
*********************************************************************************************************
* 函 数 名: tcp_callback
* 功能说明: TCP Socket的回调函数
* 形    参: soc  TCP Socket类型
*             evt  事件类型
*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址
*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号
* 返 回 值: 
*********************************************************************************************************
*/
U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)
{
char buf[50];
uint16_t i;

/* 确保是socket_tcp的回调 */
if (soc != socket_tcp) 
{
return (0);
}


switch (evt) 
{
/*
远程客户端连接消息
   1、数组ptr存储远程设备的IP地址,par中存储端口号。
   2、返回数值1允许连接,返回数值0禁止连接。
*/
case TCP_EVT_CONREQ:
sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);
printf_debug("IP:%s  port:%d\r\n", buf, par);
return (1);

/* 连接终止 */
case TCP_EVT_ABORT:
break;

/* Socket远程连接已经建立 */
case TCP_EVT_CONNECT:
printf_debug("Socket is connected to remote peer\r\n");
break;

/* 连接断开 */
case TCP_EVT_CLOSE:
  printf_debug("Connection has been closed\r\n");
break;

/* 发送的数据收到远程设备应答 */
case TCP_EVT_ACK:
break;

/* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */
case TCP_EVT_DATA:
printf_debug("Data length = %d\r\n", par);


printf_debug("接收到的数据:%sr\n",  ptr);
memcpy(RecData1,ptr,par);
RecLen1 = par;
break;
}

return (0);
}


uint8_t TCP_SendData(uint8_t *dat,uint16_t len)
{
uint8_t res = 0;
uint16_t maxlen = 0;
uint8_t *sendbuf;
res = tcp_check_send(socket_tcp);

if(res == TRUE)
{
maxlen = tcp_max_dsize(socket_tcp);
printf("可以发送的最大报文长度:%d\n",maxlen);
sendbuf = tcp_get_buf(len);
memcpy(sendbuf,dat,len);
tcp_send(socket_tcp,sendbuf,len);

}

}






void TCPnetTCPServerTest(void)
{
uint8_t res = 0;
uint8_t tcp_status;
/* 初始化网络协议栈 */
init_TcpNet ();

/* 
  创建TCP Socket并创建监听,客户端连接服务器后,10秒内无数据通信将断开连接。
  但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。
*/
    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);
if(socket_tcp != 0)
{
res = tcp_listen (socket_tcp, PORT_NUM);
printf_debug("tcp listen res = %d\r\n", res);
}


/* 创建一个周期是100ms的软定时器 */
    bsp_StartAutoTimer(0, 200);

while(1)
{
/* TCP轮询 */
tcpnet_poll1();

/* 用于网线插拔的处理 */
tcp_status = TCP_StatusCheck();
if(RecLen1 > 0)
{
TCP_SendData(RecData1,RecLen1);
RecLen1 = 0;

}
}

}


main函数

int main(void)
{
bsp_Init();
bsp_InitDMAUart1();


bsp_StartTimer(1,500);

TCPnetTCPServerTest();
//TCPnetTest();
}

四、测试结果

开发板上面IP 设置为192.168,1,15

ping 的结果

RL-TCPnet之TCP服务器(enc28j60)

收发数据

RL-TCPnet之TCP服务器(enc28j60)RL-TCPnet之TCP服务器(enc28j60)


五、完整代码

http://download.csdn.net/download/chen244798611/10106587点击打开链接