0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

有线网络通信实验4之TCP服务器

汽车电子技术 来源:滑小稽笔记 作者:电子技术园地 2023-03-01 14:26 次阅读

有关于TCP协议的知识在上一章已经有过描述,这里我们直接使用API来实现TCP服务器模式。

27.1 实验例程

27.1.1 tcp_server.c代码编写

#include "tcp_server.h"
#include "delay.h"
#include "usart1.h"
#include "lcd.h"
#include "malloc.h"
#include "string.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/memp.h"
#include "lwip/mem.h"
#include "comm.h"
//TCP Server 测试全局状态标记变量
//bit7:0,没有数据要发送;1,有数据要发送
//bit6:0,没有收到数据;1,收到数据了.
//bit5:0,没有客户端连接上;1,有客户端连接上了.
//bit4~0:保留
u8 tcp_server_flag;
//关闭tcp连接
void tcp_server_connection_close( struct tcp_pcb *tpcb, struct tcp_server_struct *es )
{
  tcp_close( tpcb ) ;
  tcp_arg( tpcb, NULL ) ;
  tcp_sent( tpcb, NULL ) ;
  tcp_recv( tpcb, NULL ) ;
  tcp_err( tpcb, NULL ) ;
  tcp_poll( tpcb, NULL, 0 ) ;
  if( es )
    mem_free( es ) ; 
  tcp_server_flag &= ~( 1<<5 ) ;                      //标记连接断开了
}
// tcp_recv函数的回调函数
u8 tcp_server_recvbuf[ TCP_SERVER_RX_BUFSIZE ] ;                //TCP Server接收数据缓冲区
err_t tcp_server_recv( void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err )
{
  err_t ret_err ;
  u32 data_len = 0 ;
  struct pbuf *q ;
    struct tcp_server_struct *es ;
  LWIP_ASSERT( "arg != NULL", arg != NULL ) ;
  es = ( struct tcp_server_struct* )arg ;
  //从客户端接收到空数据
  if( p==NULL )
  {
    es->state = ES_TCPSERVER_CLOSING ;                //需要关闭TCP连接了
    es->p = p ; 
    ret_err = ERR_OK ;
  }
  //从客户端接收到一个非空数据,但是由于某种原因err!=ERR_OK
  else if( err!=ERR_OK )
  {
    if( p )
      pbuf_free( p ) ;                        //释放接收pbuf
    ret_err = err ;
  }
  //处于连接状态
  else if( es->state==ES_TCPSERVER_ACCEPTED )
  {
    //当处于连接状态并且接收到的数据不为空时将其打印出来
    if( p!=NULL )
    {
      memset( tcp_server_recvbuf, 0, TCP_SERVER_RX_BUFSIZE ) ;      //数据接收缓冲区清零
      //遍历完整个pbuf链表
      for( q=p; q!=NULL; q=q->next )
      {
        if( q->len>( TCP_SERVER_RX_BUFSIZE-data_len ) )
          memcpy( tcp_server_recvbuf+data_len, q->payload, TCP_SERVER_RX_BUFSIZE-data_len ) ; 
        else
          memcpy(tcp_server_recvbuf+data_len, q->payload, q->len ) ;
        data_len += q->len ;
        //超出TCP客户端接收数组,跳出
        if( data_len>TCP_SERVER_RX_BUFSIZE )
          break ;
      }
      tcp_server_flag |= 1<<6 ;                    //标记接收到数据了
      lwipdev.remoteip[ 0 ] = tpcb->remote_ip.addr&0xFF ;        //IADDR4
      lwipdev.remoteip[ 1 ] = ( tpcb->remote_ip.addr>>8 )&0xFF ;    //IADDR3
      lwipdev.remoteip[ 2 ] = ( tpcb->remote_ip.addr>>16 )&0xFF ;    //IADDR2
      lwipdev.remoteip[ 3 ] = ( tpcb->remote_ip.addr>>24 )&0xFF ;    //IADDR1 
       tcp_recved( tpcb, p->tot_len ) ;                  //用于获取接收数据
      pbuf_free( p ) ;                        //释放内存
      ret_err = ERR_OK ;
    }
  }
  //服务器关闭了
  else
  {
    tcp_recved( tpcb, p->tot_len  );                    //用于获取接收数据
    es->p=  NULL ;
    pbuf_free( p ) ;                          //释放内存
    ret_err = ERR_OK ;
  }
  return ret_err ;
}
// tcp_err函数的回调函数
void tcp_server_error( void *arg, err_t err )
{  
  LWIP_UNUSED_ARG( err ) ;
  printf( "tcp error:%x\\r\\n", ( u32 )arg ) ;
  //释放内存
  if( arg!=NULL )
    mem_free( arg ) ;
}
//发送数据
void tcp_server_senddata( struct tcp_pcb *tpcb, struct tcp_server_struct *es )
{
  struct pbuf *ptr ;
  u16 plen ;
  err_t wr_err = ERR_OK ;
   while( ( wr_err==ERR_OK )&&( es->p )&&( es->p->len<=tcp_sndbuf( tpcb ) ) )
   {
    ptr = es->p ;
    wr_err = tcp_write( tpcb, ptr->payload, ptr->len, 1 ) ;
    if( wr_err==ERR_OK )
    { 
      plen = ptr->len ;
      es->p = ptr->next ;                      //指向下一个pbuf
      //pbuf的ref加一
      if( es->p )
        pbuf_ref( es->p ) ;
      pbuf_free( ptr ) ;
      tcp_recved( tpcb, plen ) ;                    //更新tcp窗口大小
    }
    else if( wr_err==ERR_MEM )
      es->p = ptr ;
   }
}
// tcp_poll的回调函数
const u8 *tcp_server_sendbuf = "STM32F103 TCP Server send data\\r\\n" ;      //TCP服务器发送数据内容
err_t tcp_server_poll( void *arg, struct tcp_pcb *tpcb )
{
  err_t ret_err;
  struct tcp_server_struct *es ; 
  es = ( struct tcp_server_struct* )arg ;
  if( es!=NULL )
  {
    //判断是否有数据要发送
    if( tcp_server_flag&( 1<<7 ) )
    {
      es->p = pbuf_alloc( PBUF_TRANSPORT, strlen( ( char* )tcp_server_sendbuf ), PBUF_POOL ) ; 
      pbuf_take( es->p, ( char* )tcp_server_sendbuf, strlen( ( char* )tcp_server_sendbuf ) ) ;
      tcp_server_senddata( tpcb, es ) ;                  //轮询的时候发送要发送的数据
      tcp_server_flag &= ~( 1<<7 ) ;                  //清除数据发送标志位
      if( es->p!=NULL )
        pbuf_free( es->p ) ;                    //释放内存  
    }
    //关闭操作
    else if( es->state==ES_TCPSERVER_CLOSING )
      tcp_server_connection_close( tpcb, es ) ;              //关闭连接
    ret_err = ERR_OK ;
  }
  else
  {
    tcp_abort( tpcb ) ;                          //终止连接,删除pcb控制块
    ret_err = ERR_ABRT ; 
  }
  return ret_err ;
}
// tcp_sent的回调函数(远端收到ACK信号后发送数据)
err_t tcp_server_sent( void *arg, struct tcp_pcb *tpcb, u16_t len )
{
  struct tcp_server_struct *es ;
  LWIP_UNUSED_ARG( len ) ;
  es = ( struct tcp_server_struct * )arg ;
  if( es->p )
    tcp_server_senddata( tpcb, es ) ;                    //发送数据
  return ERR_OK ;
}
//强制删除主动断开时的time wait
extern void tcp_pcb_purge( struct tcp_pcb *pcb ) ;                //在 tcp.c里面
extern struct tcp_pcb *tcp_active_pcbs ;                    //在 tcp.c里面
extern struct tcp_pcb *tcp_tw_pcbs ;                      //在 tcp.c里面
void tcp_server_remove_timewait()
{
  struct tcp_pcb *pcb, *pcb2 ; 
  while( tcp_active_pcbs!=NULL )
  {
    lwip_periodic_handle() ;                      //继续轮询
    lwip_pkt_handle() ;
     delay_ms( 10 ) ;                          //等待tcp_active_pcbs为空  
  }
  pcb = tcp_tw_pcbs ;
  //如果有等待状态的pcbs
  while( pcb!=NULL )
  {
    tcp_pcb_purge( pcb ) ;
    tcp_tw_pcbs = pcb->next ;
    pcb2 = pcb ;
    pcb = pcb->next ;
    memp_free( MEMP_TCP_PCB, pcb2 ) ;
  }
}
// tcp_accept的回调函数
err_t tcp_server_accept( void *arg, struct tcp_pcb *newpcb, err_t err )
{
  err_t ret_err ;
  struct tcp_server_struct *es ;
   LWIP_UNUSED_ARG( arg ) ;
  LWIP_UNUSED_ARG( err ) ;
  tcp_setprio( newpcb, TCP_PRIO_MIN ) ;                                //设置新创建的pcb优先级
  es = ( struct tcp_server_struct* )mem_malloc( sizeof( struct tcp_server_struct ) ) ;  //分配内存
  //内存分配成功
   if( es!=NULL )
  {
    es->state = ES_TCPSERVER_ACCEPTED ;                //接收连接
    es->pcb = newpcb ;
    es->p = NULL ;
    tcp_arg( newpcb, es ) ;
    tcp_recv( newpcb, tcp_server_recv ) ;                  //初始化tcp_recv的回调函数
    tcp_err( newpcb, tcp_server_error ) ;                  //初始化tcp_err回调函数
    tcp_poll( newpcb, tcp_server_poll, 1 ) ;                //初始化tcp_poll回调函数
    tcp_sent( newpcb, tcp_server_sent ) ;                  //初始化发送回调函数
    tcp_server_flag |= 1<<5 ;                      //标记有客户端连上了
    lwipdev.remoteip[ 0 ] = newpcb->remote_ip.addr&0xFF ;        //IADDR4
    lwipdev.remoteip[ 1 ] = ( newpcb->remote_ip.addr>>8 )&0xFF ;      //IADDR3
    lwipdev.remoteip[ 2 ] = ( newpcb->remote_ip.addr>>16 )&0xFF ;      //IADDR2
    lwipdev.remoteip[ 3 ] = ( newpcb->remote_ip.addr>>24 )&0xFF ;      //IADDR1
    ret_err = ERR_OK ;
  }
  else
    ret_err = ERR_MEM ;
  return ret_err ;
}
// TCP Server 测试
void tcp_server_test()
{
  err_t err ;  
  struct tcp_pcb *tcppcbnew ;                        //定义一个TCP服务器控制块
  struct tcp_pcb *tcppcbconn ;                      //定义一个TCP服务器控制块
  u8 *tbuf ;
  u8 res=0 ;
  u8 connflag=0 ;                            //连接标记
  tbuf = mymalloc( SRAMIN, 200 ) ;                    //申请内存
  //内存申请失败了,直接退出
  if( tbuf==NULL )
    return ;
  sprintf( ( char* )tbuf, "Server IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
  LCD_ShowString( 30, 130, tbuf ) ;                    //服务器IP
  sprintf( ( char* )tbuf, "Server Port:%d", TCP_SERVER_PORT ) ;
  LCD_ShowString( 30, 150, tbuf ) ;                    //服务器端口
  tcppcbnew = tcp_new() ;                        //创建一个新的pcb
  //创建成功
  if( tcppcbnew )
  {
    err = tcp_bind( tcppcbnew, IP_ADDR_ANY, TCP_SERVER_PORT ) ;      //将本地IP与指定端口号绑定
    //绑定完成
    if( err==ERR_OK )
    {
      tcppcbconn = tcp_listen( tcppcbnew ) ;              //设置tcppcb进入监听状态
      tcp_accept( tcppcbconn, tcp_server_accept ) ;            //初始化tcp_accept的回调函数
    }
    else
      res = 1 ;
  }
  else
    res = 1 ;
  while( res==0 )
  {
    //收到数据
    if( tcp_server_flag&1<<6 )
    {
      tcp_server_flag |= 1<<7 ;                    //标记要发送数据
      LCD_ShowString( 30, 210, tcp_server_recvbuf ) ;          //显示接收到的数据
      tcp_server_flag &= ~( 1<<6 ) ;                  //标记数据已经被处理了
    }
    //是否连接上
    if( tcp_server_flag&1<<5 )
    {
      if( connflag==0 )
      { 
      sprintf( ( char* )tbuf, "Client IP:%d.%d.%d.%d", lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2], lwipdev.remoteip[3] ) ;
         LCD_ShowString( 30, 170, tbuf ) ;              //客户端IP
        LCD_ShowString( 30, 190, "Receive Data:" ) ;          //提示消息
        connflag = 1 ;                        //标记连接了
      }
    }
    else if( connflag )
      connflag = 0 ;                          //标记连接断开了
    lwip_periodic_handle() ;
    lwip_pkt_handle() ;
    delay_ms( 2 ) ;
  }   
  tcp_server_connection_close( tcppcbnew, 0 ) ;                //关闭TCP Server连接
  tcp_server_connection_close( tcppcbconn, 0 ) ;                //关闭TCP Server连接
  tcp_server_remove_timewait() ; 
  memset( tcppcbnew, 0, sizeof( struct tcp_pcb ) ) ;
  memset( tcppcbconn, 0, sizeof( struct tcp_pcb ) ) ;
  myfree( SRAMIN, tbuf ) ;
}

27.1.2 tcp_server.h代码编写

#ifndef _TCP_SERVER_DEMO_H_
#define _TCP_SERVER_DEMO_H_
#include "sys.h"
#include "lwip/tcp.h"
#include "lwip/pbuf.h"
#define TCP_SERVER_RX_BUFSIZE  2000          //定义tcp server最大接收数据长度
#define TCP_SERVER_PORT      8088          //定义tcp server的端口
//tcp服务器连接状态
enum tcp_server_states
{
  ES_TCPSERVER_NONE = 0,      //没有连接
  ES_TCPSERVER_ACCEPTED,      //有客户端连接上了
  ES_TCPSERVER_CLOSING,      //即将关闭连接
};
//LWIP回调函数使用的结构体
struct tcp_server_struct
{
  u8 state;              //当前连接状
  struct tcp_pcb *pcb;        //指向当前的pcb
  struct pbuf *p;          //指向接收/或传输的pbuf
}; 
void tcp_server_test( void ) ;                  //TCP Server测试函数
#endif

27.1.3 主函数代码编写

#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "tim.h"
#include "lcd.h"
#include "malloc.h"
#include "dm9000.h"
#include "lwip/netif.h"
#include "comm.h"
#include "lwipopts.h"
#include "tcp_server.h"
int main()
{
  u8 buf[ 30 ];
   STM32_Clock_Init( 9 ) ;                                        //系统时钟设置
  SysTick_Init( 72 ) ;                          //延时初始化
  USART1_Init( 72, 115200 ) ;                      //串口初始化为115200
  LCD_Init() ;                            //初始化LCD
  TIM3_Init( 1000, 719 ) ;                        //定时器3频率为100hz
  my_mem_init( SRAMIN ) ;                      //初始化内部内存池
  while( lwip_comm_init() ) ;                      //lwip初始化
  //等待DHCP获取成功/超时溢出
  while( ( lwipdev.dhcpstatus!=2 )&&( lwipdev.dhcpstatus!=0xFF ) )
  {
    lwip_periodic_handle() ;                    //LWIP内核需要定时处理的函数
    lwip_pkt_handle() ;
  }
  POINT_COLOR=RED;
  LCD_ShowString( 30, 110, "LWIP Init Successed" ) ;
  //打印动态IP地址
  if( lwipdev.dhcpstatus==2 )
    sprintf( ( char* )buf, "DHCP IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
  //打印静态IP地址
  else
    sprintf( ( char* )buf, "Static IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
  LCD_ShowString( 30, 130, buf ) ; 
  //得到网速
  if( ( DM9000_Get_SpeedAndDuplex()&0x02 )==0x02 )
    LCD_ShowString( 30, 150, "Ethernet Speed:10M" ) ;
  else
    LCD_ShowString( 30, 150, "Ethernet Speed:100M" ) ;
   while( 1 )
  {
    tcp_server_test() ;                        //TCP服务器测试
    lwip_periodic_handle() ;
    lwip_pkt_handle() ;
    delay_ms( 2 ) ;
  }
}

27.2 实验结果

图片

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 服务器
    +关注

    关注

    12

    文章

    9142

    浏览量

    85387
  • API
    API
    +关注

    关注

    2

    文章

    1499

    浏览量

    61989
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1353

    浏览量

    79066
收藏 人收藏

    评论

    相关推荐

    串口服务器——TCP Server

    。在实际应用中,我们经常会遇到需要将串口数据转发到TCP服务器或将TCP服务器数据转发到串口的情况,所以本文将介绍如何实现串口接入TCP
    的头像 发表于 07-31 17:58 1647次阅读
    串口<b class='flag-5'>服务器</b>——<b class='flag-5'>TCP</b> Server

    LABVIEW实现网络通信的方法

    ︱Variable选项,然后根据提示一步一步即可完成设置,但需要注意的是要实现网络通信变量类型必须选择为“Network-Published”。这里在服务器中创建一个名为Server_Variable的共享
    发表于 12-12 18:02

    四种在LabVIEW中实现网络通信的方法

    方便的实现数据的共享。用户无需了解任何的底层复杂的网络通信,就能轻松地实现数据交换。用户建立和使用共享变量就如同操作全局变量一样方便。  4.2 利用共享变量通信实例  以下通过C/S(客户端/服务器
    发表于 02-04 15:32

    基于LABVIEW实现网络通信的方法

    任何的底层复杂的网络通信,就能轻松地实现数据交换。用户建立和使用共享变量就如同操作全局变量一样方便。4.2 利用共享变量通信实例  以下通过C/S(客户端/服务器通信模式实现数据的传
    发表于 04-28 10:04

    为什么网络通信实验TCP客户端模式时连接不上?

    战舰V3上的实验50 网络通信实验,当用TCP服务器模式时,通信正常,用TCP客户端模式时连接不
    发表于 08-16 04:35

    如何才能让网络通信实验支持10个UDP链接?

    大家好,我在使用探索者STM32F4开发板,寄存例程,实验55 网络通信实验。我修改了例程,想创建10个UDP端口监听,udppcb=udp_new()这里最多只能创建5个,5个之后
    发表于 08-27 22:48

    为什么网络通信实验网络助手上接受到的先是456个字节?

    网络通信实验里将ARMF407配置成sever,通过电脑上网络助手接受ARM发送到数据,将tcp_sever_sendbuf[1000]改成一个1000的大数组并赋值,但是在网络助手
    发表于 09-02 02:54

    请问谁有F407网络通信实验的视频吗?

    请问有没有F407网络通信实验的视频啊,能方便给个链接或者地址吗,谢谢了,急用嘞
    发表于 09-23 01:49

    STM32F103上网络通信实验服务器与客户端连接但不能进行数据交换该怎么办?

    我在应用原子哥的103板子的网络通信实验时,发现了一个小问题,就是在服务器模式下,板子可以与很多的客户端建立连接,但是在进行数据交换时,却只能和第一个连上的客户端进行数据交换,只有在于这个客户端断开
    发表于 10-15 04:37

    为什么f4+ucosiii+lwip1.4.1连接服务器会返回错误码-10?

    f4+ucosiii+lwip1.4.1+lan8720采用原子哥的开发板带操作系统的移植代码,网络部分的执行代码也是以开发板例子“实验55 网络通信实验”为基础移植的然后开发板作为c
    发表于 10-16 01:47

    如何把stm32f407网络通信实验调通?

    最近在做网络通信实验,没有调通,有没有大神指点一下
    发表于 10-30 04:35

    请问探索者网络通信实验的控制网页该怎么写呢?

    原子哥虽然有比较详细的讲解了网络通信实验,但是对于那个控制网页怎么写的却没有过多的涉及,请问有谁会写这种控制网页的,诚心求教!
    发表于 11-06 04:35

    基于原子STM32F4的摄像头与网络通信实验

    应单片机课设要求,做了一个摄像头拍照网络通信C/S实时LCD显示。该工程基于原子STM32F4的摄像头与网络通信实验,在此基础上,将其整合。1.预期功能:摄像头拍取的内容实时传输至LCD进行显示通过
    发表于 08-03 06:04

    基于TCP/IP的网络通信应用程序分享

    -IP网络通信应用程序(TCP-Client)本章节,将以服务端的角色进行讲解,如何开发一个TCP服务端(
    发表于 12-23 08:12

    有线网络通信实验3TCP客户端

    TCP先把数据流分割成适当长度的报文段,最大传输段的长度MSS通常受计算机连接的网络的数据链路层的最大传输单元MTU控制,之后TCP才把数据包传给IP层,通过它来将数据传送给接收端的TCP
    的头像 发表于 03-01 14:27 1499次阅读
    <b class='flag-5'>有线网络通信实验</b>3<b class='flag-5'>之</b><b class='flag-5'>TCP</b>客户端