ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LWIP学习之Socket(应用篇)

2021-12-17 11:34:38  阅读:280  来源: 互联网

标签:LWIP Socket sock 学习 client 客户端 recv sin addr


LWIP学习之Socket(应用篇)

原文链接:https://blog.csdn.net/qq_39854159/article/details/120940325

Socket接口编程
https://blog.csdn.net/qq_39854159/article/details/120693512?spm=1001.2014.3001.5501

1 客户端和服务器流程图

在这里插入图片描述
在这里插入图片描述
其流程图就像打客服电话一样,服务器可以理解成中国移动客服电话一样,我们便是客户端。
服务器就需要使用Socket()注册一个电话,使用bind()绑定一个电话号码10086,listen()就是一个客服在监听着电话。然后进入accept()阻塞之中。毕竟这个时候没人打电话也不需要处理什么东西。
这个时候我们客户端也可以使用socket()注册一个电话,然后使用connect()拨打10086。
原本闲着没事的客服听到叮铃铃电话响了,就会从accept()的阻塞态跳出,期间会进行著名的TCP三握手。经过“喂~” “喂?” “喂~”之后,就可以投诉为什么给我张套餐费用巴拉巴拉……。然后10086给出他的解释。
客户端使用close()函数怒挂了电话。通过四次挥手之后,10086也使用close()挂上了电话。

代码分析:
若对以上流程图用到的基本socket API有不懂的可以看下我的另外一篇文章《Socket API篇》
这里代码就使用野火的《LwIP应用开发实战指南》中的例程。

2 服务器

 #include "tcpecho.h"

 #include "lwip/opt.h"

 #if LWIP_SOCKET
 #include <lwip/sockets.h>

 #include "lwip/sys.h"
 #include "lwip/api.h"
 /*--------------------------------------------------------------------*/

 #define PORT              5001
 #define RECV_DATA         (1024)


 static void
 tcpecho_thread(void *arg)
 {
     int sock = -1,connected;
     char *recv_data;
     struct sockaddr_in server_addr,client_addr;
     socklen_t sin_size;
     int recv_data_len;

     recv_data = (char *)pvPortMalloc(RECV_DATA); //申请1024字节内存,作为接收数据的存储区
     if (recv_data == NULL)
     {
         printf("No memory\n");                  //申请失败跳转到__exit
         goto __exit;
     }

     sock = socket(AF_INET, SOCK_STREAM, 0);    //创建一个socket套接字sock
     if (sock < 0)
     {
         printf("Socket error\n");				//创建失败跳转到__exit
         goto __exit;
     }

     server_addr.sin_family = AF_INET;        //选择IPV4协议族
     server_addr.sin_addr.s_addr = INADDR_ANY;//允许任何地址链接
     server_addr.sin_port = htons(PORT);      //设置端口号5001
     memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

     if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)//使用bind()绑定之前配置的信息
     {
         printf("Unable to bind\n");
         goto __exit;
     }

     if (listen(sock, 5) == -1)//sock最多监听5个链接
     {
         printf("Listen error\n");
         goto __exit;
     }

     while (1)
     {
         sin_size = sizeof(struct sockaddr_in);

         connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);//阻塞等待客户端链接,若没有链接任务会在此进入一个阻塞态。

         printf("new client connected from (%s, %d)\n",
             inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));//打印客户端的地址端口信息
         {
             int flag = 1;

             setsockopt(connected,
                     IPPROTO_TCP,     /* set option at TCP level */
                     TCP_NODELAY,     /* name of option */
                     (void *) &flag, /* the cast is historical cruft */
                     sizeof(int));    /* length of option value */
         }

         while (1)
         {
             recv_data_len = recv(connected, recv_data, RECV_DATA, 0);
             //客户端连接上后会在此阻塞,等待接收数据

             if (recv_data_len <= 0)
             //若客户端断开连接,客户端发送FIN包,recv()会返回-1,跳出这个while循环回到accept()阻塞
                 break;

             printf("recv %d len data\n",recv_data_len);

             write(connected,recv_data,recv_data_len);//写数据

         }
         if (connected >= 0)
             closesocket(connected);//若之前有客户的连接,这里会关闭客户端连接

         connected = -1;
     }
 __exit:
     if (sock >= 0) closesocket(sock);//服务器socket创建成功,则关闭服务器socket
     if (recv_data) free(recv_data);//若接收内存申请成功,则释放接收内存
 }

 void
 tcpecho_init(void)
 {
     sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 512, 4);
 }

3 客户端

 #include "client.h"

 #include "lwip/opt.h"

 #include "lwip/sys.h"
 #include "lwip/api.h"

 #include <lwip/sockets.h>

 #define PORT              5001
 #define IP_ADDR        "192.168.0.181"

 static void client(void *thread_param)
 {
     int sock = -1;
     struct sockaddr_in client_addr;

     uint8_t send_buf[]= "This is a TCP Client test...\n";

     while (1)
     {
         sock = socket(AF_INET, SOCK_STREAM, 0);//创建一个客户端socket叫sock
         if (sock < 0)
         {//若创建失败延时10ms继续创建
             printf("Socket error\n");
             vTaskDelay(10);
             continue;
         }

         client_addr.sin_family = AF_INET;//服务器设置的地址协议族IPV4
         client_addr.sin_port = htons(PORT);//服务器设置的端口号
         client_addr.sin_addr.s_addr = inet_addr(IP_ADDR);//服务器的地址
         memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));

         if (connect(sock,
                     (struct sockaddr *)&client_addr,
                     sizeof(struct sockaddr)) == -1)//连接服务器
         {//若连接失败则关闭socket()延时10ms跳出while(1)
             printf("Connect failed!\n");
             closesocket(sock);
             vTaskDelay(10);
             continue;
         }

         printf("Connect to iperf server successful!\n");

         while (1)
         {
             if (write(sock,send_buf,sizeof(send_buf)) < 0)//写数据
                 break;

             vTaskDelay(1000);
         }

         closesocket(sock);//关闭套接字
     }

 }

 void
 client_init(void)
 {
     sys_thread_new("client", client, NULL, 512, 4);
 }

4 不足之处

以上是野火提供的socket的用法是重复型服务器,一次只能处理一个连接。当其他客户端使用connect()连接之后,若客户端不close(),那么服务器便会阻塞在recv()函数处。正确客户端连接便不会被服务器响应。

5 解决思路

使用select()函数。在recv()之前使用select()先检测是否正确的连接到来。
select()函数的返回值

当监视的相应的文件描述符集中满足条件时,比如说读文件描述符集中有数据到来时,内核(I/O)根据状态修 改文件描述符集,并返回一个大于0 的数。
当没有满足条件的文件描述符,且设置的timeval 监控时间超时时,select函数会返回一个为0的值。
当select返回负值时,发生错误。
现在FD队列中添加需要监控的客户端,如按键和com端口。
例子参考:

    FD_ZERO(&rds); 
    FD_SET(button_fd,&rds); //添加按键fd
    FD_SET(com,&rds)//添加com fd

    ret=select(fd+1,&rds,NULL,NULL,NULL); 

    if(ret<0) 
    { 
        printf("select failure\n"); 
        exit(1); 
    } 

    if(ret==0) 
    { 
        printf("select timeout\n"); 

    } 
     
    else if(FD_ISSET(button_fd,&rds)) //按键客户端连接
    { 
        read(button_fd,&but_status,sizeof(but_status));           
    } 
  
 
   else if(FD_ISSET(com_fd,&rds)) //com客户端连接
    { 
        read(.......);           
    } 

标签:LWIP,Socket,sock,学习,client,客户端,recv,sin,addr
来源: https://blog.csdn.net/m0_46577050/article/details/121992022

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有