基于w5500实现TCP/IP协议后应用层开发

基于w5500实现TCP/IP协议后应用层开发

一、TCP Server/Client
首先需要搞清楚什么是服务端,什么是客户端。服务器端,指网络中能为用户提供服务的计算机系统,是为客户端服务的;客户端是指与服务器相对应,它是接受服务的一段,为客户提供本地服务的程序。看图就明白啦。在这里插入图片描述
此时服务器处于守候状态,并侦听客户端的请求。客户端发出请求,并请求经互联网送给服务器。一旦服务器接收到这个请求,就可以执行请求所制定的任务,并将执行的结果经互联网回送给客户。
TCP 协议通过三个报文段完成连接的建立,这个过程称为三次握手(three-way handshake)。建立一个连接需要三次握手,而终止一个连接要经过四次挥手,这是由 TCP的半关闭(half-close)造成的。
在这里插入图片描述
1.1w5500作为tcp server
流程图如下:
在这里插入图片描述
第一步先初始化socket,socket处于SOCK_INIT 状态。
第二步作为TCP服务器就要执行listen()函数来侦听端口。socket进入SYN_RCVD状态。
第三步等到TCP三次握手完成便建立连接了。socket进入ESTABLISHED状态。现在可以进行数据的收发了
第四步,数据通信完成后,执行disconnect()函数,该socket进入SOCK_CLOSE_WAIT状态。
第五步连接出错或者超时,将会进入入 SOCK_CLOSE 状态。

*@brief		TCP Server回环演示函数。
*@param		无
*@return*/
void do_tcp_server(void)
{	
	uint16 len=0;  
	switch(getSn_SR(SOCK_TCPS))											            	/*获取socket的状态*/
	{
		case SOCK_CLOSED:													                  /*socket处于关闭状态*/
			socket(SOCK_TCPS ,Sn_MR_TCP,local_port,Sn_MR_ND);	        /*打开socket*/
		  break;     
    
		case SOCK_INIT:														                  /*socket已初始化状态*/
			listen(SOCK_TCPS);												                /*socket建立监听*/
		  break;
		
		case SOCK_ESTABLISHED:												              /*socket处于连接建立状态*/
		
			if(getSn_IR(SOCK_TCPS) & Sn_IR_CON)//接收中断,类似于串口接收中断。
			{
				setSn_IR(SOCK_TCPS, Sn_IR_CON);								          /*清除接收中断标志位*/
			}
			len=getSn_RX_RSR(SOCK_TCPS);									            /*定义len为已接收数据的长度*/
			if(len>0)
			{
				recv(SOCK_TCPS,buff,len);								              	/*接收来自Client的数据*/
				buff[len]=0x00; 											                  /*添加字符串结束符*/
				printf("%s\r\n",buff);
				send(SOCK_TCPS,buff,len);									              /*向Client发送数据*/
		  }
		  break;
		
		case SOCK_CLOSE_WAIT:												                /*socket处于等待关闭状态*/
			close(SOCK_TCPS);
		  break;
	}
}
//这里可以通过getSn_SR(SOCK_TCPS)这个函数来获得此时socket的状态。

#define SOCK_TCPS             0
#define SOCK_HUMTEM			  0
#define SOCK_PING			  0
#define SOCK_TCPC             1
#define SOCK_UDPS             2
#define SOCK_WEIBO      	  2
#define SOCK_DHCP             3
#define SOCK_HTTPS            4
#define SOCK_DNS              5
#define SOCK_SMTP             6
#define SOCK_NTP              7

可以看到这里的8个socket通过宏定义都有了自己的任务。如果socket的状态进入到上文提到的第三步也就是established状态,就可以实现客户端和服务端的通信了。使用如下两个函数,收发

*@brief		This function is an application I/F function which is used to receive the data in TCP mode.
					It continues to wait for data as much as the application wants to receive.
*@param		s: socket number.
*@param		buf: data buffer to receive.
*@param		len: data length.
*@return  received data size for success else 0.
*/
uint16 recv(SOCKET s, uint8 * buf, uint16 len)
{
   uint16 ret=0;
   if ( len > 0 )
   {
      recv_data_processing(s, buf, len);
      IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_RECV);
      /* wait to process the command... */
      while( IINCHIP_READ(Sn_CR(s) ));
      /* ------- */
      ret = len;
   }
   return ret;
}

可以看出很多接收数据的函数都类似啊,变量就是socket号,缓存的数组和接收的字节数。
同时可以看到len=getSn_RX_RSR(SOCK_TCPS)这个函数是用来得到接收缓存区字节数大小的一个函数。他的brief是This fuction is to give size of received data in receive buffer.所以用这个函数来判断缓存区是否有数据到达。
同样也有发送函数即 uint16 send(SOCKET s, const uint8 * buf, uint16 len),发送成功返回值为1,失败为0.
1.2 w5500作为TCP client
其实也是差不多的流程,一样需要刚刚提到的getSn_SR(socket号)函数来获取socket号状态。只不过没有侦听(listen)的过程了,需要连接(connect)的过程。看下图:
在这里插入图片描述
第一步先选择ip信息,例程里提供了几种ip来源

#define IP_FROM_DEFINE	        0       			              /*使用初始定义的IP信息*/
#define IP_FROM_DHCP	          1       			              /*使用DHCP获取IP信息*/
#define IP_FROM_EEPROM	        2       			              /*使用EEPROM定义的IP信息*/
extern uint8	ip_from;											                /*选择IP信息配置源*/

宏定义真的很方便啊,可以选择ip的来源。
1.3 w5500 用作UDP
在这里插入图片描述
1.将UDP模式下的socket端口初始化,进入SOCK_UDP状态。此时就可以通过广播方式发送数据了。
使用getSn_SR函数获取状态,输入量为socket端口序号,如果获取到udp端口的状态 SOCK_CLOSED,则初始化端口,使用函数uint8 socket(SOCKET s, uint8 protocol, uint16 port, uint8 flag)进行初始化,SOCKET s:为socket端口号,protocol:为选择的协议,port:为远程端口还是本地端口,flag为0;
2.进入SOCK_UDP状态后就可以发送和接收数据了。
不好意思过了好久才更新,最近一直忙项目。

1.4 DHCP
W5500作为DHCP的客户端,路由器作为DHCP的服务端。在 DHCP请求的过程中,包括 4 个主要的阶段:发现阶段、提供阶段、选择阶段以及确认阶段。
首先 W5500 客户端发送 DHCPDISCOVER 消息(IP 地址租用申请),这个消息通过广播方式发出。

/**
*@brief		发送DISCOVER信息给DHCP服务器
*@param		无
*@return	无
*/
void send_DHCP_DISCOVER(void)

使用这个函数就可以了。
应用在检查DHCP状态上

switch ( dhcp_state )
	{
		case STATE_DHCP_READY:													  /*DHCP初始化状态*/
			DHCP_timeout = 0;																/*DHCP超时标志设置为1*/
			reset_DHCP_time();															/*复位超时时间*/
			send_DHCP_DISCOVER();														/*发送DISCOVER包*/
	
			DHCP_timer = 0;																	/*set_timer0(DHCP_timer_handler);  */ 	
			dhcp_state = STATE_DHCP_DISCOVER;								/*DHCP的DISCOVER状态*/
			break;	 

当客户端由初始化状态进入到STATE_DHCP_DISCOVER状态,就可以等待服务器回应的DHCPOFFER消息。

	case STATE_DHCP_DISCOVER:
			if (type == DHCP_OFFER) 
			{
				reset_DHCP_time();														/*复位超时时间*/
				send_DHCP_REQUEST();													/*发送REQUEST包*/
				dhcp_state = STATE_DHCP_REQUEST;
				#ifdef DHCP_DEBUG			
					printf("state : STATE_DHCP_REQUEST\r\n");
				#endif			
			}
			else 
				check_DHCP_Timeout();													/*检查是否超时*/
			break;

type是刚刚解析收到的封包类型。接下来进入到了送 DHCPREQUEST状态,这个状态下在 DHCPREQUEST 消息中将包含客户端申请的 IP 地址。

	case STATE_DHCP_REQUEST :						/*DHCP的REQUEST状态*/
			if (type == DHCP_ACK) 					/*接收到DHCP服务器回应的off封包*/
			{
				reset_DHCP_time();               //DHCP定时初始化
				if (check_leasedIP()) 
				{
					#ifdef DHCP_DEBUG					
						printf("state : STATE_DHCP_LEASED\r\n");
					#endif		
					dhcp_state = STATE_DHCP_LEASED;//当检查ip不冲突后,进入ip租赁状态
					return DHCP_RET_UPDATE;  //获取地址成功
				} 
				else 
				{
					#ifdef DHCP_DEBUG					
						printf("state : STATE_DHCP_DISCOVER\r\n");
					#endif				
					dhcp_state = STATE_DHCP_DISCOVER;
					return DHCP_RET_CONFLICT;
				}
			}	
			else if (type == DHCP_NAK) //如果收不到ack回应包,就继续请求
			{
				reset_DHCP_time();										/*复位超时时间*/
				dhcp_state = STATE_DHCP_DISCOVER;
				#ifdef DHCP_DEBUG				
					printf("state : STATE_DHCP_DISCOVER\r\n");
				#endif			
			}
			else 
				check_DHCP_Timeout();													/*检查是否超时*/
			break;

最后,DHCP 服务器将回送 DHCPACK 的响应消息来通知客户端可以使用该 IP 地址,该确认里面包含了分配的 IP 地址和该地址的一个稳定期限的租约(默认是 8 天),并同时更新 DHCP 数据库。
主程序的话依然是状态机编程,相比之前的tcp,udp,DHCP还是多了很多状态的复杂了。

void do_dhcp(void)
{

	uint8 dhcpret=0;
	ip_from=IP_FROM_DHCP;	/*IP配置方法选择为DHCP*/
	dhcp_timer_init();																 /*初始化DHCP定时器*/
	if(Conflict_flag == 1)
	{
		init_dhcp_client();				                       /*初始化DHCP客户端*/ 
		Conflict_flag =0;
	}
	
	dhcpret = check_DHCP_state(SOCK_DHCP);             /*获取DHCP服务状态*/
	
	switch(dhcpret)
	{
		case DHCP_RET_NONE:                              /*IP地址获取不成功*/       
			break;
		
		case DHCP_RET_TIMEOUT:                           /*IP地址获取超时*/ 
			break;
		
		case DHCP_RET_UPDATE:														 /*成功获取到IP地址*/ 
		  dhcp_ok=1;                  
			set_w5500_ip();                                /*将获取到的IP地址写入W5500寄存器*/ 
			printf(" 已从DHCP服务器成功获得IP地址\r\n");

	    break;
		
		case DHCP_RET_CONFLICT:                          /*IP地址获取冲突*/ 
			printf(" 从DHCP获取IP地址失败\r\n");
      dhcp_state = STATE_DHCP_READY; 
      printf(" 重试中\r\n");
      dhcp_ok=0; 
			break;     

		default:
			break;
	}

}

对应刚才获取dhcp服务器状态的继续状态机,如果获取成功就将ip地址通过spi写入w5500寄存器。

1.5DNS域名服务器
在这里插入图片描述
通过DNS把相当于把网站的ip地址变成网站网址。

/**
*@brief		 查询DNS报文信息,解析来自DNS服务器的回复
*@param		 s:DNS服务器socket,name:要解析的信息
*@return	 成功: 返回1, 失败 :返回 -1
*/
uint8 dns_query(uint8 s, uint8 * name)
{
  static uint32 dns_wait_time = 0;
  struct dhdr dhp;																												/*定义一个结构体用来包含报文头信息*/
  uint8 ip[4];
  uint16 len, port;
  switch(getSn_SR(s))																											/*获取socket状态*/
  {
		case SOCK_CLOSED:
			dns_wait_time = 0;
			socket(s, Sn_MR_UDP, 3000, 0);																			/*打开W5500的socket的3000端口,并设置为UDP模式*/
			break;  
		
    case SOCK_UDP:																												/*socket已打开*/
			len = dns_makequery(0, name, BUFPUB, MAX_DNS_BUF_SIZE);							/*接收DNS请求报文并存入BUFPUB*/
			sendto(s, BUFPUB, len, EXTERN_DNS_SERVERIP, IPPORT_DOMAIN);					/*发送DNS请求报文给DNS服务器*/
      if ((len = getSn_RX_RSR(s)) > 0)
			{
        if (len > MAX_DNS_BUF_SIZE) len = MAX_DNS_BUF_SIZE;
				len = recvfrom(s, BUFPUB, len, ip, &port);												/*接收UDP传输的数据并存入BUFPUB*/
        if(parseMSG(&dhp, BUFPUB))																				/*解析DNS响应信息*/
				{
          close(s);																												/*关闭socket*/
          return DNS_RET_SUCCESS;																					/*返回DNS解析成功域名信息*/
				}
        else 
					dns_wait_time = DNS_RESPONSE_TIMEOUT;														/*等待响应时间设置为超时*/
      }
      else
      {
        delay_ms(1000);																										/*没有收到DNS服务器的UDP回复,避免太频繁,延时1s*/
        dns_wait_time++;																									/*DNS响应时间加1*/
      }
      if(dns_wait_time >= DNS_RESPONSE_TIMEOUT)														/*如果DNS等待时间超过3s*/
      {
        close(s);																													/*关闭socket*/
        return DNS_RET_FAIL;
      }
      break;
			
  }
  return DNS_RET_PROGRESS;
}

烂尾了
后面的NTP,NetBIOS,HTTP Client,HTTP Server,ICMP以后再讲吧。

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值