广告
返回顶部
首页 > 资讯 > 服务器 >Lwip TCP/UDP客户端、服务器详解
  • 934
分享到

Lwip TCP/UDP客户端、服务器详解

服务器tcp/ipudp单片机网络协议 2023-09-24 16:09:06 934人浏览 独家记忆
摘要

一、TCP客户端         tcp客户端实现是比较简单的,大致分为以下几个步骤:      (1)申请套接字。      (2)绑定远端服务器的ip地址和端口。      (3)连接远端服务器。      (4)接收和发送数据。 #d

一、TCP客户端

        tcp客户端实现是比较简单的,大致分为以下几个步骤:

     (1)申请套接字。

     (2)绑定远端服务器的ip地址和端口。

     (3)连接远端服务器。

     (4)接收和发送数据。

#define PORT 5001#define RECV_DATA (1024)#define SERV_IP_ADDR "192.168.31.39"#define SERV_PORT 5001void tcp_client(void *arg){  int sock=-1;  struct sockaddr_in Serv_addr;  char*recv_data;  int recv_data_len;    recv_data=(char*)pvPortMalloc(RECV_DATA);  if(recv_data==NULL){      printf("Mallo memory failed\r\n");  }  while(1){            sock=Socket(AF_INET,SOCK_STREAM,0);    if(sock<0)    {      printf("Socket error\n");      vTaskDelay(10);      continue;    }        Serv_addr.sin_family=AF_INET;        Serv_addr.sin_port=htons(SERV_PORT);        Serv_addr.sin_addr.s_addr=inet_addr(SERV_IP_ADDR);      memset(&(Serv_addr.sin_zero), 0, sizeof(Serv_addr.sin_zero));          if (connect(sock, (struct sockaddr *)&Serv_addr, sizeof(struct sockaddr)) == -1)     {printf("Connect failed!\n");closesocket(sock);vTaskDelay(10);continue;    }       printf("Connect to tcp server successful!\n");        while(1)    {      recv_data_len = recv(sock, recv_data, RECV_DATA, 0);if (recv_data_len <= 0)         break;       printf("recv:%s\n",recv_data);write(sock,recv_data,recv_data_len);    }  }  }

现象:

 电脑作为TCP服务器,单片机为TCP客户端来连接服务器,通过电脑服务端往单片机发送112233、555533,单片机接收到消息后将消息原路发送给电脑。

部分函数解析:

(1)int socket(int domain,int type,int protocol)

该函数用于申请套接字。

参数domain:套接字采用的协议簇,常用的有AF_INET--ipv4      AF_INET6--ipv6

参数type:套接字采用的服务类型,SOCK_STREAM表示可靠的面对连接的socket连接(TCP),SOCK_DGRAM提供面向消息的无保障连接(UDP),SOCK_RAW表示原始的套接字。

参数protocol:套接字所采用的协议,在TCP/UDP两种协议下均为0。

返回值:套接字申请成功返回Socket描述符(int类型) 失败返回-1。

(2)htons、ntohs、htonl、ntohl

这些函数用于大小端转换。

htons:host to network short long,将主机字节序转化成网络字节序,即将无符号16位整型转化成大端模数。

ntohs:network  to host short long,将网络字节序转化成主机字节序,即将大端模式转化成无符号16位整型。

htonl:host to network long 

ntohl:network to host long

htonl、ntohl两函数也类似,只不过是在无符号32位整型与网络字节序之间转换。

(3)inet_ntoa、inet_addr

inet_ntoa:将无符号32位地址数据(uint32_t)转化成char*类型的字符串

inet_addr:将char*类型的字符串转化成无符号32位地址数据(uint32_t)。

(4) memset (void *, int, size_t)

该函数用于将一段内存中的值全转化成指定的值,此处 memset(&(Serv_addr.sin_zero), 0, sizeof(Serv_addr.sin_zero));将结构体sockaddr_in中成员sin_zero[SIN_ZERO_LEN]赋值为0,用于保证sockaddr与sockaddr_in两个数据结构保持大小相同。

(5)connect

该函数用于连接远端服务器,参数1为所申请的套接字,参数2为远端服务器,参数3为远端服务器字节长度。

(6)recv

该函数用于TCP接收数据,参数1为所申请的套接字,参数2为接收内存空间的起始地址,参数3为内存的大小,若成功接收数据返回数据长度,若接收失败返回-1。

(7)write

该函数TCP发送数据,参数1为所申请的套接字,参数2为发送数据的起始地址,参数3为发送数据的大小。

二、TCP服务器

        TCP服务器创建步骤如下:

      (1)申请套接字

      (2)绑定服务器本地的ip、端口等信息

      (3)监听连接请求,与客户端连接

      (4)接收和发送数据

void tcp_serv(void *arg){    struct sockaddr_in serv_addr,client_addr;    char *recv_data;    int sock=-1;    int remote_sock;    socklen_t client_addr_len;    int recv_data_len;      client_addr_len = sizeof(struct sockaddr_in);      recv_data = (char *)pvPortMalloc(RECV_DATA);           sock = socket(AF_INET, SOCK_STREAM, 0);    if(sock < 0){        printf("Socket 创建失败, 错误代码:%d\n", errno);    }else{        printf("Socket 创建成功");    }             serv_addr.sin_addr.s_addr = INADDR_ANY;             serv_addr.sin_family = AF_INET;              serv_addr.sin_port = htons(PORT); // 小端模式转为大端模式             memset(&(serv_addr.sin_zero), 0, sizeof(serv_addr.sin_zero));         if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)    {        printf("Unable to bind\r\n");        Goto __exit;    }        if (listen(sock, 5) == -1)    {        printf("Listen error\r\n");        goto __exit;    }    while (1){            remote_sock = accept(sock, (struct sockaddr *) &client_addr, &client_addr_len);        printf("new client connected from (%s, %d)\n",        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));        {        int flag = 1;        setsockopt(remote_sock,        IPPROTO_TCP,         TCP_nodeLAY,         (void *) &flag,         sizeof(int));         }        while (1)        {                recv_data_len = recv(remote_sock, recv_data, RECV_DATA, 0);        if (recv_data_len <= 0)        break;        printf("recv %d len data\n",recv_data_len);                write(remote_sock,recv_data,recv_data_len);        }        if (remote_sock >= 0)        closesocket(remote_sock);        remote_sock = -1;    } __exit:if (sock >= 0) closesocket(sock);if (recv_data) free(recv_data);}

现象:

电脑作为TCP客户端,单片机作为TCP服务端,通过串口打印可知其ip地址为192.168.31.48,用TCP客户端 来连接该ip,连接成功后往单片机分别发送5555、6666,单片机收到消息以后原路发回客户端。需要注意TCP连接为面向连接的、可靠的通讯协议,服务器一次只能与一个客户端可靠连接,但是可以同时有多个客户端申请连接(等待连接)。

部分函数解释:

  (1) serv_addr.sin_addr.s_addr = INADDR_ANY

INADDR_ANY表示地址0.0.0.0,泛指本地的所有ip地址,上述代码即绑定本地的所有网卡IP地址作为服务器地址,因为有时候本地连接的不止一张网卡。

(2)socklen_t client_addr_len = sizeof(struct sockaddr_in)

此处要特别注意,不能初始化为NULL,否则无法接收客户端的ip等信息!!!!

三、UDP服务器

        udp是一种无连接、不可靠的协议,因此其连接与TCP相比更为简单,但是数据的传输没有保障,多用于音频、视频等数据数据传输(传输速度快)。

        udp服务器创建步骤如下:

      (1)申请套接字

      (2)绑定服务器本地ip、端口等信息

      (3)接收、发送数据

static void udp_serv(void *arg){  int sock=-1;;  char*recv_data;  struct sockaddr_in udp_addr,client_addr;  int recv_data_len;  socklen_t client_addr_len=sizeof(struct sockaddr);//必须初始化,否则无法接收  while(1){    recv_data=(char*)pvPortMalloc(RECV_DATA);//开辟接收缓存区  返回缓存区首地址    if (recv_data == NULL)      {      printf("No memory\n");      goto __exit;      }        sock=socket(AF_INET,SOCK_DGRAM,0);    if(sock<0){      printf("Socker error\n");      goto __exit;      }            udp_addr.sin_family=AF_INET;            udp_addr.sin_addr.s_addr=INADDR_ANY;            udp_addr.sin_port=htons(PORT);      memset(&(udp_addr.sin_zero),0,sizeof(udp_addr.sin_zero));            if(bind(sock,(struct sockaddr*)&udp_addr,sizeof(struct sockaddr)) == -1)      {      printf("Unable to bind\n");      goto __exit;      }      while(1){                recv_data_len=recvfrom(sock,recv_data,RECV_DATA,0,(struct sockaddr*)&client_addr,&client_addr_len);                printf("receive from %s\n",inet_ntoa(client_addr.sin_addr));//char *ip4addr_ntoa(const ip4_addr_t *addr);将32位地址数据转化char*类型的字符串            //ipaddr_addr(const char *cp)将char*类型的字符串转化成32位地址数据                printf("recevce:%s",recv_data);                sendto(sock,recv_data,recv_data_len,0,(struct sockaddr*)&client_addr,client_addr_len);      }  }__exit:      if (sock >= 0) closesocket(sock);      if (recv_data) free(recv_data);}

现象:

 

 由于UDP是一种无连接的协议,因此UDP服务器可以同时被多个UDP客户端“连接”,这里单片机作为UDP服务器,我分别将手机和电脑作为两个UDP客户端,同时连接单片机,并隔500ms发送一次数据,通过电脑端和手机端的网络调试助手可知,单片机在接收到数据后均原路发回了,串口调试端打印的信息:192.168.31.151为手机端UDP的ip地址,192.168.31.39为电脑端UDP的ip地址,可见UDP协议的传输速度是比较快的。

部分函数解释:

(1)socket

此处采用的是UDP连接,第二个参数为SOCK_DGRAM

(2)recvfrom、sendto

这两个函数用于UDP连接中接收和发送数据。

recvfrom(int s,void *mem,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen)

参数s:连接的套接字

参数mem:保存接收数据的内存首地址

参数len:接收数据缓冲区大小

参数from:保存消息发送端的ip、port等信息

参数fromlen:client_addr的大小

返回值:接收成功,返回接收数据长度,失败返回-1

 sendto(int s,const void *dataptr,size_t size,int flags,const struct sockaddr *to,socklen_t tolen)

参数s:连接的套接字

参数dataptr:需要发送的数据首地址

参数size:发送数据缓冲区大小

参数to:接收端的ip、port等信息

参数tolen:client_addr的大小

返回值:接收成功,返回发送数据长度,失败返回-1

四、UDP客户端

        因为UDP为无连接协议,因此直接往指定服务器发送数据即可。

        创建UDP客户端步骤如下:

       (1)申请创建套接字

       (2)绑定远端服务器ip、端口等信息

       (3)往服务器发送数据和接收数据

char test_buf[]="sense_long is nb";static void udp_client(void *arg){  int sock=-1;  //char*recv_data;  struct sockaddr_in Serve_addr;  //int recv_data_len;  socklen_t addrlen=sizeof(struct sockaddr);//必须初始化,否则无法接收**************************  while(1){    //recv_data=(char*)pvPortMalloc(RECV_DATA);//开辟接收缓存区    //if (recv_data == NULL)    //  {     // printf("No memory\n");     // goto __exit;     // }    sock=socket(AF_INET,SOCK_DGRAM,0);//创建udp套接字    if(sock<0){      printf("Socker error\n");      goto __exit;      }      Serve_addr.sin_family=AF_INET;             Serve_addr.sin_addr.s_addr=inet_addr("192.168.31.151");             Serve_addr.sin_port=htons(PORT);      memset(&(Serve_addr.sin_zero),0,sizeof(Serve_addr.sin_zero));      //sendto(sock,test_buf,sizeof(test_buf),0,(struct sockaddr*)&Serve_addr,addrlen);   while(1){       // recv_data_len=recvfrom(sock,recv_data,RECV_DATA,0,(struct sockaddr*)&Serve_addr,&addrlen);                //printf("receive from %s\n",inet_ntoa(Serve_addr.sin_addr));//char *ip4addr_ntoa(const ip4_addr_t *addr);将32位地址数据转化char*类型的字符串            //ipaddr_addr(const char *cp)将char*类型的字符串转化成32位地址数据               // printf("recevce:%s",recv_data);                sendto(sock,test_buf,sizeof(test_buf),0,(struct sockaddr*)&Serve_addr,addrlen);        vTaskDelay(1000);      }  }__exit:      if (sock >= 0) closesocket(sock);      //if (recv_data) free(recv_data);}

现象如下:

单片机作为udp服务器,向服务器1s发送一条“sense_long is nb”。 

注意:上述实验均需要在同一子网(局域网)内进行,若需要跨网段通讯需要借助云平台。

电脑本地的ip地址可以通过CMD来获得,在cmd中输入ipconfig即可打印本地的ip地址

如果上述文章有帮助到您,麻烦点一个赞叭~

来源地址:https://blog.csdn.net/weixin_46461874/article/details/128201892

--结束END--

本文标题: Lwip TCP/UDP客户端、服务器详解

本文链接: https://www.lsjlt.com/news/416967.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作