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

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

3天内不再提示

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

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

26.1 TCP协议概述

TCP是一种面向连接的,可靠地,基于IP的传输层协议,面向连接就意味着两个实用TCP的应用在进行数据交换的时候必须先建立一个TCP连接,当应用层向TCP层发送用于传输的,用8位字节表示的数据流,TCP先把数据流分割成适当长度的报文段,最大传输段的长度MSS通常受计算机连接的网络的数据链路层的最大传输单元MTU控制,之后TCP才把数据包传给IP层,通过它来将数据传送给接收端的TCP层。为了保证报文传输的可靠性,会给每个包一个序号,同时序号也保证了传送到接收端的数据报文能被按照顺序接收,然后接收端对成功接收的报文发回一个响应的确认ACK,如果传送端在合理的时间RTT内没有收到确认,那么对应的数据就会被重传TCP在数据正确性和合法性上采用一个校验和函数来测定数据是否有错误,在发送和接收时都必须计算校验和,在保证可靠性上,对于窗口内未经确认的分组需要重传报文,在拥塞控制上,采用TCP拥塞控制算法。

TCP数据被封装在一个IP数据报文中,IP数据报文结构如下图所示。

图片

TCP报文数据格式在没有选项的情况下,通常是20个字节,数据结构如下图所示

图片

(1)源端口号和目的端口号用于寻找发送端和接收端的应用进程,这个与UDP报文相同,这两个值加上IP首部中的源IP地址和目的IP地址唯一确定了一个TCP连接。

(2)序列号字段用来标识从TCP发送端向TCP接收端发送的数据字节流,用于表示在这个报文段中的第一个数据字节,当建立一个新的连接时,握手报文中的SYN标志置1,这个握手报文中的序号字段为随机选择的初始序号ISN(Initial Sequence Number),当连接建立好以后发送方要发送的第一个字节序号为ISN+1。

(3)确认号字段只有在ACK为1的时候才有用,确认号中包含发送确认的一方所期望接收到的下一个序号,确认号是在上一次成功接收到的数据字节序列号上加1,例如上次接收成功接收到对方发过来的数据序号为X,那么返回的确认号就应该为X+1

(4)头部长度又称为首部长度,首部长度中给出了首部的长度,以4个字节为单位,这个字段有4bit,因此TCP最多有60字节的首部,如果没有任何的选项字段,正常的首部长度是20字节,TCP首部中还有6个标志位,这6个标志位如下表所示。

标志位 说明
URG 置1时表示紧急指针有效
ACK 置1时表示确认序号字段有效
PSH 置1表示接收方应该尽快将这个报文段交给应用层
RST 置1表示重建连接
SYN 用于发起连接
FIN 发送端完成发送任务,终止连接

(5)窗口尺寸也就是窗口大小,其中填写相应的值以通知对方期望接收的字节数,窗口大小字段是TCP流量控制的关键字段,窗口大小是一个2个字节的字段,因此窗口大小最大为65535个字节。

(6)16位校验和和UDP的校验和计算原理相同,这是一个强制性的字段,校验和覆盖整个TCP报文段。

(7)紧急指针只有在URG置1时有效,是一个正偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。

tcp.c,tcp.h,tcp_in.c和tcp_out.c是LWIP中关于TCP协议的文件,TCP层中函数的关系如下图所示。

图片

常用的TCP协议的API函数如下表所示。

函数类型 API函数 功能
建立TCP连接 tcp_new() 创建一个TCP的PCB控制块
tcp_bind() 为TCP的PCB控制块绑定一个本地IP地址和端口号
tcp_listen() 开始TCP的PCB监听
tcp_accept() 控制块accept字段注册的回调函数,侦听到连接时被调用
tcp_accepted() 通知LWIP协议栈一个TCP连接被接受了
tcp_conect() 连接远端主机
TCP数据发送 tcp_write() 构造一个报文并放在控制块的发送队列缓冲中
tcp_sent() 控制块sent字段注册的回调函数,数据发送成功后被回调
tcp_output() 将发送缓冲队列中的数据发送出去
TCP数据接收 tcp_recv() 控制块recv字段注册的回调函数,当接收到新数据时被调用
tcp_recved() 当程序处理完数据后一定要调用这个函数,通知内核更新接收窗口
数据轮询 tcp_poll() 控制块poll字段注册的回调函数,该函数周期性调用
关闭和终止连接 tcp_close() 关闭TCP连接
tcp_err() 控制块err字段注册的回调函数,遇到错误时被调用
tcp_abort() 中断TCP连接

26.2 应用编写

26.2.1 tcp_client.c代码编写

#include "tcp_client.h"
#include "delay.h"
#include "usart1.h"
#include "lcd.h"
#include "malloc.h"
#include "string.h"
#include "comm.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/memp.h"
#include "lwip/mem.h"
//TCP Client 测试全局状态标记变量
//bit7:0,没有数据要发送;1,有数据要发送
//bit6:0,没有收到数据;1,收到数据了
//bit5:0,没有连接上服务器;1,连接上服务器了
//bit4~0:保留
u8 tcp_client_flag;   
//设置远端IP地址
void tcp_client_set_remoteip()
{
  u8 *tbuf;
  tbuf=mymalloc( SRAMIN, 100 ) ;                      //申请内存
  if( tbuf==NULL )
    return ;
  //前三个IP保持和DHCP得到的IP一致
  lwipdev.remoteip[ 0 ] = lwipdev.ip[ 0 ] ;
  lwipdev.remoteip[ 1 ] = lwipdev.ip[ 1 ] ;
  lwipdev.remoteip[ 2 ] = lwipdev.ip[ 2 ] ;
  lwipdev.remoteip[ 3 ] = 113 ;
  sprintf( ( char* )tbuf, "Remote IP:%d.%d.%d.", lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2] ) ;
  LCD_ShowString( 30, 150, tbuf ) ;                    //远端IP
  myfree( SRAMIN, tbuf ) ;
}
//关闭与服务器的连接
void tcp_client_connection_close( struct tcp_pcb *tpcb, struct tcp_client_struct *es )
{
  tcp_abort( tpcb ) ;                            //终止连接,删除pcb控制块
  tcp_arg( tpcb, NULL ) ;
  tcp_recv( tpcb, NULL ) ;
  tcp_sent( tpcb, NULL ) ;
  tcp_err( tpcb, NULL ) ;
  tcp_poll( tpcb, NULL, 0 );
  if( es )
    mem_free( es ) ;
  tcp_client_flag &= ~( 1<<5 ) ;                      //标记连接断开了
}
// tcp_recv函数的回调函数
u8 tcp_client_recvbuf[ TCP_CLIENT_RX_BUFSIZE ] ;                //接收数据缓冲区
err_t tcp_client_recv( void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err )
{
  u32 data_len=0 ;
  struct pbuf *q ;
  struct tcp_client_struct *es ;
  err_t ret_err ;
  LWIP_ASSERT( "arg != NULL", arg!=NULL ) ;
  es = ( struct tcp_client_struct* )arg ;
  //如果从服务器接收到空的数据帧就关闭连接
  if( p==NULL )
  {
    es->state = ES_TCPCLIENT_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_TCPCLIENT_CONNECTED )
  {
    //当处于连接状态并且接收到的数据不为空时
    if( p!=NULL )
    {
      memset( tcp_client_recvbuf, 0, TCP_CLIENT_RX_BUFSIZE ) ;      //数据接收缓冲区清零
      //遍历完整个pbuf链表
      for( q=p; q!=NULL; q=q->next )
      {
        if( q->len>( TCP_CLIENT_RX_BUFSIZE-data_len ) )
          memcpy( tcp_client_recvbuf+data_len, q->payload, TCP_CLIENT_RX_BUFSIZE-data_len ) ; 
        else
          memcpy( tcp_client_recvbuf+data_len, q->payload, q->len ) ;
        data_len += q->len ;
        //超出TCP客户端接收数组,跳出
        if( data_len>TCP_CLIENT_RX_BUFSIZE )
          break ;
      }
      tcp_client_flag |= 1<<6 ;                    //标记接收到数据了
       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_client_error( void *arg, err_t err )
{


}
//发送数据
void tcp_client_senddata( struct tcp_pcb *tpcb, struct tcp_client_struct *es )
{
  struct pbuf *ptr ; 
   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 )
    {
      es->p = ptr->next ;                      //指向下一个pbuf
      //pbuf的ref加一
      if( es->p )
        pbuf_ref( es->p );
      pbuf_free( ptr ) ;                        //释放ptr
    }
    else if( wr_err==ERR_MEM )
      es->p = ptr ;
    tcp_output( tpcb ) ;                        //发送缓冲队列中的数据发送
  }
}
// tcp_sent的回调函数(从远端接收到ACK后发送数据)
err_t tcp_client_sent( void *arg, struct tcp_pcb *tpcb, u16_t len )
{
  struct tcp_client_struct *es ;
  LWIP_UNUSED_ARG( len ) ;
  es = ( struct tcp_client_struct* )arg ;
  if( es->p )
    tcp_client_senddata( tpcb, es ) ;                    //发送数据
  return ERR_OK ;
}
// tcp_poll的回调函数
const u8 *tcp_client_sendbuf = "STM32F103 TCP Client send data\\r\\n" ;      //TCP服务器发送数据内容
err_t tcp_client_poll( void *arg, struct tcp_pcb *tpcb )
{
  err_t ret_err ;
  struct tcp_client_struct *es ; 
  es = ( struct tcp_client_struct* )arg ;
  //连接处于空闲可以发送数据
  if( es!=NULL )
  {
    //判断是否有数据要发送
    if( tcp_client_flag&( 1<<7 ) )
    {
      es->p = pbuf_alloc( PBUF_TRANSPORT, strlen( ( char* )tcp_client_sendbuf ), PBUF_POOL ) ; 
      pbuf_take( es->p, ( char* )tcp_client_sendbuf, strlen( ( char* )tcp_client_sendbuf ) ) ; 
      tcp_client_senddata( tpcb, es ) ;                  //将数据发送出去
      tcp_client_flag &= ~( 1<<7 ) ;                  //清除数据发送标志
      //释放内存
      if( es->p )
        pbuf_free( es->p ) ;
    }
    else if( es->state==ES_TCPCLIENT_CLOSING )
       tcp_client_connection_close( tpcb, es ) ;              //关闭TCP连接
    ret_err = ERR_OK ;
  }
  else
  { 
    tcp_abort( tpcb ) ;                          //终止连接,删除pcb控制块
    ret_err = ERR_ABRT ;
  }
  return ret_err ;
}
//连接建立后调用回调函数
err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
  struct tcp_client_struct *es=NULL;  
  if(err==ERR_OK)   
  {
    es = ( struct tcp_client_struct* )mem_malloc( sizeof( struct tcp_client_struct ) ) ; //申请内存
    //内存申请成功
    if( es )
    {
       es->state = ES_TCPCLIENT_CONNECTED ;            //状态为连接成功
      es->pcb = tpcb ;
      es->p = NULL ;
      tcp_arg( tpcb, es ) ;                      //更新tpcb的callback_arg
      tcp_recv( tpcb, tcp_client_recv ) ;                  //初始化tcp_recv回调功能
      tcp_err( tpcb, tcp_client_error ) ;                  //初始化tcp_err()回调函数
      tcp_sent( tpcb, tcp_client_sent ) ;                  //初始化tcp_sent回调功能
      tcp_poll( tpcb, tcp_client_poll, 1 ) ;                //初始化tcp_poll回调功能
       tcp_client_flag |= 1<<5 ;                    //标记连接到服务器了
      err = ERR_OK ;
    }
    else
    {
      tcp_client_connection_close( tpcb, es ) ;              //关闭连接
      err = ERR_MEM ;                        //返回内存分配错误
    }
  }
  else
    tcp_client_connection_close( tpcb, 0 ) ;                //关闭连接
  return err ;
}
//客户机测试
void tcp_client_test()
{
   struct tcp_pcb *tcppcb ;                        //定义一个TCP服务器控制块
  struct ip_addr rmtipaddr ;                        //远端ip地址
  u8 *tbuf ;
  u8 res=0 ;    
  u8 t=0 ; 
  u8 connflag=0 ;                            //连接标记
  tcp_client_set_remoteip() ;                        //先选择IP
  tbuf = mymalloc( SRAMIN, 200 ) ;                    //申请内存
  //内存申请失败了,直接退出
  if( tbuf==NULL )
    return ;
  sprintf( ( char* )tbuf, "Local 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, "Remote IP:%d.%d.%d.%d", lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2], lwipdev.remoteip[3] ) ;
  LCD_ShowString( 30, 150, tbuf ) ;                    //远端IP
  sprintf( ( char* )tbuf, "Remote Port:%d", TCP_CLIENT_PORT ) ;          //客户端端口号
  LCD_ShowString( 30, 170, tbuf ) ;
  LCD_ShowString( 30, 190, "STATUS:Disconnected" ) ;
  tcppcb = tcp_new() ;                          //创建一个新的pcb
  //创建成功
  if( tcppcb )
  {
  IP4_ADDR( &rmtipaddr, lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2], lwipdev.remoteip[3] ) ;
    tcp_connect( tcppcb, &rmtipaddr, TCP_CLIENT_PORT, tcp_client_connected ) ; 
   }
  else
    res = 1 ;
  while( res==0 )
  {
    //是否收到数据
    if( tcp_client_flag&1<<6 )
    {
      LCD_ShowString( 30, 230, tcp_client_recvbuf ) ;            //显示接收到的数据
      tcp_client_flag |= 1<<7 ;                    //标记要发送数据
      tcp_client_flag &= ~( 1<<6 ) ;                  //标记数据已经被处理了
    }
    //是否连接上
    if( tcp_client_flag&1<<5 )
    {
      if( connflag==0 )
      { 
        LCD_ShowString( 30, 190, "STATUS:Connected   " ) ;
        LCD_ShowString( 30, 210, "Receive Data:" ) ;
        connflag = 1 ;                        //标记连接了
      }
    }
    else if( connflag )
    {
       LCD_ShowString( 30, 190, "STATUS:Disconnected" ) ;
      connflag = 0 ;                          //标记连接断开了
    } 
    lwip_periodic_handle() ;
    lwip_pkt_handle() ;
    delay_ms( 2 ) ;
    t ++ ;
    if( t==200 )
    {
      //未连接上,则尝试重连
      if( ( connflag==0 )&&( ( tcp_client_flag&1<<5 )==0 ) )
      { 
        tcp_client_connection_close( tcppcb, 0 ) ;            //关闭连接
        tcppcb = tcp_new() ;                    //创建一个新的pcb
        //创建成功
        if( tcppcb )
          tcp_connect( tcppcb, &rmtipaddr, TCP_CLIENT_PORT, tcp_client_connected ) ; 
      }
      t = 0 ;
    }    
  }
  tcp_client_connection_close( tcppcb, 0 ) ;                  //关闭TCP Client连接
  myfree( SRAMIN, tbuf ) ;
}

26.2.2 tcp_client.h代码编写

#ifndef _TCP_CLIENT_H_
#define _TCP_CLIENT_H_
#include "sys.h"
#include "lwip/tcp.h"
#include "lwip/pbuf.h"
#define TCP_CLIENT_RX_BUFSIZE  1500            //最大接收数据长度
#define TCP_CLIENT_TX_BUFSIZE  200              //最大发送数据长度
#define LWIP_SEND_DATA      0x80            //有数据发送
#define  TCP_CLIENT_PORT    8087            //远端端口
//tcp服务器连接状态
enum tcp_client_states
{
  ES_TCPCLIENT_NONE = 0,    //没有连接
  ES_TCPCLIENT_CONNECTED,  //连接到服务器了 
  ES_TCPCLIENT_CLOSING,    //关闭连接
};
//LWIP回调函数使用的结构体
struct tcp_client_struct
{
  u8 state;            //当前连接状
  struct tcp_pcb *pcb;      //指向当前的pcb
  struct pbuf *p;        //指向接收/或传输的pbuf
};  
void tcp_client_test( void ) ;                    //TCP Client测试函数
#endif

26.2.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_client.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_client_test() ;
    lwip_periodic_handle() ;
    lwip_pkt_handle() ;
    delay_ms( 2 ) ;
  }
}

26.3 实验结果

图片

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

    关注

    8

    文章

    1348

    浏览量

    78979
  • MSS
    MSS
    +关注

    关注

    0

    文章

    5

    浏览量

    6717
  • 传输层协议
    +关注

    关注

    0

    文章

    6

    浏览量

    1253
收藏 人收藏

    评论

    相关推荐

    LABVIEW实现网络通信的方法

    网络通信,可以使用的通信协议类型包括TCP/IP协议、UDP、串口通信协议、无线网络协议等;(2)使用基于
    发表于 12-12 18:02

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

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

    labview TCP客户端

    最近在做一个labview 客户端测试小程序,服务器采用MFC编写,客户端采用TCP侦听函数,通信可以连接,数据也正确,但是服务器检测发送
    发表于 06-30 23:15

    【NanoPi NEO试用体验】TCP通信客户端程序

    写了一个TCP通信客户端程序,匆忙之间写的,不够完善,可以围观,给出精辟的见解。百度百科TCP/IP介绍:http://baike.baidu.com/link?url
    发表于 12-28 23:40

    android的Tcp网络通信

    在所有的通信中,网络通信是用到的比较多的一种,这一节我们主要是在广州微嵌安卓工业平板上学习是如何进行Tcp通信的。Tcp
    发表于 12-05 10:49

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

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

    请问在做网络通信试验时,PC一般是客户端还是服务器?

    原子哥以及论坛里面的各位大神,请问在做网络通信试验时,PC一般是客户端还是服务器?单片机这边应该是客户端还是服务器?
    发表于 06-27 02:12

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

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

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

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

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

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

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

    网络通信C/S方式将STM32摄像头拍取的照片传输到电脑在PC开发可视化界面接受摄像头数据并更新显示2.实施步骤:1.参考STM32原子摄像头实验
    发表于 08-03 06:04

    stm32f107vc lwip tcp客户端

    模拟的服务器,实现简单的数据收发,通过上位机控制板子的LED灯一 打开工程《科星F107开发板网络应用篇TCP客户端》打开M
    发表于 08-06 09:17

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

    基于TCP/IP的网络通信应用程序(TCP-Server)上一篇文章讲述了在i.MX6UL开发板中,以客户端的角色,使用TCP/IP协议进行
    发表于 12-23 08:12

    网络调试和串口调试集合UDP TCP客户端TCP服务器应用程序免费下载

    本文档的主要内容详细介绍的是网络调试和串口调试集合UDP TCP客户端TCP服务器应用程序免费下载。
    发表于 08-30 08:00 16次下载
    <b class='flag-5'>网络</b>调试和串口调试集合UDP <b class='flag-5'>TCP</b><b class='flag-5'>客户端</b>和<b class='flag-5'>TCP</b>服务器<b class='flag-5'>端</b>应用程序免费下载

    网络通信的特点

    网络通信可以分为两大类:客户端客户端(C/S)和Peer-To-Peer(P2P)网络通信。其中,客户端/服务器式
    发表于 05-08 15:12 2262次阅读