tcpserver广播发送数据给所有client_WIFI模块开发教程之W600网络篇2:AP模式下TCP Server通信...

前言

本文研究如何在AP模式下进行TCP Server通信,所谓AP模式是说模块起来一个softAP热点,可以供其他WIFI设备连接,当其他设备连接成功后,另WIFI模块作为服务端,等待局域网中其他客户端连接后通信。

一、 理论基础

本节要处理的有两个问题,其一是如何利用RT_Thread连接路由器,其二是如何使用Socket套接字编程搞定TCP Server程序编写。

1.连接路由器

模块需要开启station,并且连接到一个路由器,RT_Thread中只需要调用wlan.mgnt.h中的函数即可。

    rt_wlan_set_mode(RT_WLAN_DEVICE_AP_NAME, RT_WLAN_AP);        rt_wlan_start_ap("sand", "12345678");

2.Socket套接字实现TCP Server

服务端套接字使用流程以及和客户端交互框图如下所示:

6b463eb5343597efe4f3d48075f3a2c0.png

二、使用实例

1.程序

w60x/applications/4-ap_tcp_server/main.c

#include #include #include  //使用BSD socket需要包含此头文件#define TCP_SERVER_ADDR "192.168.169.1"#define TCP_SERVER_PORT 8089static void tcp_client_thread_entry(void *args){    int ret = 0;    int fd = (int) args;    int len = 0;    fd_set readfds;    char buf[512] = { 0x00 };    struct timeval t;    t.tv_sec = 5;    t.tv_usec = 0;        while (1)    {        FD_ZERO( &readfds );        FD_SET( fd, &readfds );        ret = select( fd+1, &readfds, NULL, NULL, &t);        if ((ret > 0) && FD_ISSET(fd, &readfds))        {            len = recv(fd, buf, sizeof(buf), 0);            if (len > 0)            {                buf[len] = 0x00;                rt_kprintf("receive data:%s", buf);            }else            {                rt_kprintf("receive data from tcp server error!");                goto exit;            }            if (-1 != fd)            {                rt_sprintf(buf, "%s", buf);                ret = send(fd, buf, strlen(buf), 0);                if (ret < 0)                {                    rt_kprintf("send error, closee socket");                    goto exit;                }            }        }    }        exit:    if (-1 != fd)    {        closesocket( &fd );        fd = -1;    }}static void tcp_server_thread_entry(void *args){    int ret = 0;    int fd = -1, client_fd = -1;    struct sockaddr_in server_addr, client_addr;    struct  timeval t;    fd_set readfds;    socklen_t sockaddr_t_size;    char buf[512] = { 0x00 };    int len = 0;reconnect:    fd = socket(AF_INET, SOCK_STREAM, 0);    if (-1 == fd)    {        rt_kprintf("create socket error!!!");        goto exit;    }    server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(TCP_SERVER_PORT);    server_addr.sin_addr.s_addr = INADDR_ANY;    rt_memset(&server_addr.sin_zero, 0x00, sizeof(server_addr.sin_zero));    ret = bind(fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr ));    if (0 == ret)    {        rt_kprintf("bind success");    }else    {        rt_kprintf("connect error!!!");        goto try_reconnect;    }        ret = listen(fd, 5);    if (-1 == ret)    {        rt_kprintf("Listen error");        goto try_reconnect;    }    t.tv_sec = 2;    t.tv_usec = 0;    while (1)    {        FD_ZERO(&readfds);        FD_SET(fd, &readfds);        ret = select(fd + 1, &readfds, 0, 0, &t);        if (-1 == ret)        {            rt_kprintf("select() error!");            goto try_reconnect;        }        // else if(0 == ret)        // {        //     rt_kprintf("select() timeout!");        // }        else if(ret > 0)        {            if (FD_ISSET(fd, &readfds));            {                client_fd = accept(fd, (struct sockaddr *)&client_addr, &sockaddr_t_size);                if (client_fd < 0)                {                    rt_kprintf("accept connection failed! errno = %d", errno);                    continue;                }                rt_kprintf("client connected, ip:%s, port:%d", inet_ntoa(client_addr), ntohs(client_addr.sin_port));                //create client thread                rt_thread_t uart_thread = rt_thread_create("clients", tcp_client_thread_entry, client_fd, 4*1024, 25, 10);                if (uart_thread != NULL)                {                    rt_thread_startup(uart_thread);                }else                {                    ret = RT_ERROR;                    rt_kprintf("create tcp client error!!!");                    closesocket(fd);                }            }        }    }try_reconnect:    if (-1 != fd)    {        closesocket(fd);    }    rt_thread_sleep(1);    goto reconnect;exit:    if (-1 != fd)    {        closesocket(fd);    }    rt_kprintf("thread server exit!");}int main(void){    rt_err_t ret = RT_EOK;    char str[] = "hello world!";    // create ap    rt_wlan_set_mode(RT_WLAN_DEVICE_AP_NAME, RT_WLAN_AP);    rt_wlan_start_ap("sand", "12345678");    //create server    rt_thread_t uart_thread = rt_thread_create("server", tcp_server_thread_entry, RT_NULL, 4*1024, 25, 10);    if (uart_thread != NULL)    {        rt_thread_startup(uart_thread);    }else    {        ret = RT_ERROR;        rt_kprintf("create tcp client error!!!");    }exit:    return ret;}

2.目标文件配置

前面几个事例都是修改了RT_Thread默认路径下w60x/applications/main.c下的文件,然后使用scons编译,考虑到代码的开源,之后会统一放到github上,调整代码为如下所示:

55d5a650b3ee0207912b3b2a8faad25c.png

在main.c外增加一个文件夹,直接输入scons会提示编译错误,此时需要修改aplications/SConscript脚本。

我们先看下默认的SConscript脚本内容:

Import('RTT_ROOT')Import('rtconfig')from building import *cwd = GetCurrentDir()src  = Glob('*.c')CPPPATH = [cwd]group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)Return('group')

现在要编译4-ap_tcp_server中的main.c,需修改SConscript中第6行代码为src = Glob('4-ap_tcp_server/main.c'),之前为*.c表示applications文件目录下所有.c文件,修改后编译的时候仅编译4-ap_tcp_server/main.c,不编译其他文件内容,修改后SConscript内容如下所示:

Import('RTT_ROOT')Import('rtconfig')from building import *cwd = GetCurrentDir()src  = Glob('4-ap_tcp_server/main.c')CPPPATH = [cwd]group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)Return('group')

三、下载运行

在ENV控制台,输入scons命令,在build/Bin目录下生成rtthread_1M.FLS,

烧录运行后,电脑连接模块起来的热点,然后打开电脑网络调试助手,开启两个个TCP client,指定TCP Server的IP均为192.168.169.1,端口为8089,通过网络助手发送数据给模块,,模块收到后,会加上"",然后返回给网络助手。

网络助手界面如下:

3fcfa6751cabcb44c3cf12ca9004c63a.png

模块调试串口信息如下:

71b4141f03b54034d6d85cfa9c611ec7.png

上午图片表示,网络助手1发送device1 say hello给模块,网络助手2发送device2 say hello给模块,模块分别回应两者对应的消息。

四、结语

本节完,实际操作过程中需要注意的地方有如下几点:

(1) 注意模块每次accept到一个客户端连接后,都会创建一个相同的线程,处理和当前客户端之后的数据交互问题,所以本demo支持一个模块的TCP Server和多个TCP Client进行数据交互

(2) 设备调试串口信息中有一条:receive data from tcp server error!,这个是由于手动关闭了一个TCP Client,随后重新开启,模块日志检测到client connected,说明操作正常

作者:小驿

寄语:不管是写程序还是做人,都要坚信:方法总比问题多!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值