stm32f407+lan8720 和 python 实现多个TCP客户端连接的TCP服务器

        最近本人想用开发板来做服务器,所以就想到这个方法。对于写pc端服务器的童鞋来说,这应该是件很容易的事情,所以,这里主要分为两种实现方法:

第一种:在stm32f4开发板实现,基于lwip


硬件:正点原子stm32f407开发板(带网络功能)

lan芯片:lan8720

系统:rt-thread


第二种:在Python上实现

 

第一种:基于stm32f407 + lan8720 实现多个客户端连接的TCP服务器

主要在官方的例子上进行修改,源文件为:examples\network\tcpserver.c 

官方的程序主要是支持单路TCP客户端的连接。现在我们来修改原来的程序,暂且先扩大的支持的连接数为10个吧

        首先更改需求前,我们应该要想清楚应该怎么去做这件事情。对我来说的话,线程是最好的解决方式。每来一个链接,我就增加一个线程来处理这个连接。一旦这个客户端出现异常或者是关闭,则立即释放该线程。这里的话,就涉及到一个内存的问题,因为本身单片机的空间就不大,所以就不适合用于处理很多个的连接。在该实验中,只验证了10个客户端的请求,如果感兴趣的童鞋,也可以试试更多的。

大致的思路如下:

 

先建立一个TCP服务器,然后等待新的TCP客户端连接,超时10S

当有新的TCP客户端请求连接时,先判断是否超过最大的连接数

符合要求时,再往下判断当前的conn_id是否被占用,这里是起了一个数组来判断,当connected[conn_id] = -1时,代表这个位置是可以空闲的。

最后,创建一个线程来处理该连接的请求。异常,则释放该资源。

需要注意的是:tcp_server的线程空间为2K,每个客户端的线程空间为1K,因为客户端的线程是在堆空间的,所以不占用tcp_server的空间。

 

修改的代码如下:

#include <rtthread.h>
#include <string.h>

//#if !defined(SAL_USING_POSIX)
//#error "Please enable SAL_USING_POSIX!"
//#else
//#include <sys/time.h>
//#include <sys/select.h>
//#endif

#include <sys/socket.h> /* 使用BSD socket,需要包含socket.h头文件 */
#include "netdb.h"

#define DEBUG_TCP_SERVER

#define DBG_ENABLE
#define DBG_SECTION_NAME               "TCP"
#ifdef DEBUG_TCP_SERVER
#define DBG_LEVEL                      DBG_LOG
#else
#define DBG_LEVEL                      DBG_INFO /* DBG_ERROR */
#endif
#define DBG_COLOR
#include <rtdbg.h>

#define BUFSZ       (1024)
#define MAX_CONNECT_NUM     10  //最大连接数

static int started = 0;
static int is_running = 0;
static int port = 5000;
static const char send_data[] = "This is TCP Server from RT-Thread."; /* 发送用到的数据 */
int ret;
static char *recv_data; /* 用于接收的指针,后面会做一次动态分配以请求可用内存 */
static int sock,  bytes_received;
static int connected[MAX_CONNECT_NUM] = {0};//-1,说明该链接为空,可以执行连接
static struct sockaddr_in server_addr;
static struct sockaddr_in client_addr[MAX_CONNECT_NUM];
static struct timeval timeout;
static fd_set readset[MAX_CONNECT_NUM], readset_c[MAX_CONNECT_NUM];

static rt_uint8_t current_connnect_id = 0;
static rt_uint8_t total_connect_count = 0;
static void tcp_server_handler_msg(void *parameter)
{
    rt_uint8_t id = *(rt_uint8_t *)parameter;
    struct timeval client_timeout;
    LOG_I("\tcp_server_handler_msg : id =  %d...\n", id);
    client_timeout.tv_sec = 0;
    client_timeout.tv_usec = 1000 * 50;
    while(1)
    {
        FD_ZERO(&readset_c);
        FD_SET(connected[id], &readset_c[id]);
        /* Wait for read or write */
        if (select(connected[id] + 1, &readset_c[id], RT_NULL, RT_NULL, &client_timeout) == 0)
        {
            rt_thread_delay(10);
            continue;
        }

        /* 从connected socket中接收数据,接收buffer是1024大小,但并不一定能够收到1024大小的数据 */
        bytes_received = recv(connected[id], recv_data, BUFSZ, 0);
        if (bytes_received < 0)
        {
            LOG_E("Received error, close the connect.");
            closesocket(connected[id]);
            connected[id] = -1;
            total_connect_count --;
            break;
        }
        else if (bytes_received == 0)
        {
            /* 打印recv函数返回值为0的警告信息 */
            LOG_W("Received warning, recv function return 0.");
            continue;
        }
        else
        {
            /* 有接收到数据,把末端清零 */
            recv_data[bytes_received] = '\0';
            if (strcmp(recv_data, "q") == 0 || strcmp(recv_data, "Q") == 0)
            {
                /* 如果是首字母是q或Q,关闭这个连接 */
                LOG_I("Got a 'q' or 'Q', close the connect.");
                closesocket(connected[id]);
                connected[id] = -1;
                total_connect_count --;
                break;
            }
            else if (strcmp(recv_data, "exit") == 0)
            {
                /* 如果接收的是exit,则关闭整个服务端 */
                closesocket(connected[id]);
                connected[id] = -1;
                total_connect_count --;
            }
            else
            {
                /* 在控制终端显示收到的数据 */
                LOG_D("Received (%s:%d) data = %s----[id=%d]----", inet_ntoa(client_addr[id].sin_addr), 
                                                    ntohs(client_addr[id].sin_port),
                                                    recv_data,id);
            }
            
            /* 发送数据到connected socket */
            ret = send(connected[id], send_data, rt_strlen(send_data), 0);
            if (ret < 0)
            {
                LOG_E("send error, close the connect.");
                closesocket(connected[id]);
                connected[id] = -1;
                total_connect_count --;
                break;
            }
            else if (ret == 0)
            {
                /* 打印send函数返回值为0的警告信息 */
                LOG_W("Send warning, send function return 0.");
            }
        }
    }
}


static void tcpserv(void *arg)
{
    socklen_t sin_size = sizeof(struct sockaddr_in);

    recv_data = rt_malloc(BUFSZ + 1); /* 分配接收用的数据缓冲 */
    if (recv_data == RT_NULL)
    {
        LOG_E("No memory");
        return;
    }
    rt_memset(connected,-1,sizeof(connected));//-1,说明该链接为空,可以执行连接
    /* 一个socket在使用前,需要预先创建出来,指定SOCK_STREAM为TCP的socket */
    if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
    {
        LOG_E("Create socket error");
        goto __exit;
    }
    /* 初始化服务端地址 */
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port); /* 服务端工作的端口 */
    server_addr.sin_addr.s_addr = INADDR_ANY;
    rt_memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero));

    /* 绑定socket到服务端地址 */
    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
    {
        LOG_E("Unable to bind");
        goto __exit;
    }

    /* 在socket上进行监听 */
    if (listen(sock, MAX_CONNECT_NUM) == -1)
    {
        LOG_E("Listen error");
        goto __exit;
    }
    LOG_I("\nTCPServer Waiting for client on port %d...\n", port);
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;
    while (1)
    {
        if(total_connect_count < MAX_CONNECT_NUM)
        {
            while(connected[current_connnect_id] != -1)
            {
                LOG_I("***********the current link route status is busy! find next linker route****************\r\n");
                current_connnect_id = (current_connnect_id + 1) % MAX_CONNECT_NUM;
            }
            FD_ZERO(&readset);
            FD_SET(sock, &readset[current_connnect_id]);

            LOG_I("Waiting for a new connection,current_connnect_id=%d...",current_connnect_id);

            /* Wait for read or write */
            if (select(sock + 1, &readset[current_connnect_id], RT_NULL, RT_NULL, &timeout) == 0)
            {
                rt_thread_delay(10);
                continue;
            }

            /* 接受一个客户端连接socket的请求,这个函数调用是阻塞式的 */
            connected[current_connnect_id] = accept(sock, (struct sockaddr *)&client_addr[current_connnect_id], &sin_size);
            /* 返回的是连接成功的socket */
            if (connected[current_connnect_id] < 0)
            {
                LOG_E("accept connection failed! errno = %d", errno);
                continue;
            }

            /* 接受返回的client_addr指向了客户端的地址信息 */
            LOG_I("I got a connection from (%s , %d)\n",
                 inet_ntoa(client_addr[current_connnect_id].sin_addr), ntohs(client_addr[current_connnect_id].sin_port));
            char thread_name[8] = {0};
            sprintf((const char *)thread_name,"tsh_%d",current_connnect_id);
            rt_thread_t tid = rt_thread_create(thread_name,//"tcp_serv_handler",
                                                tcp_server_handler_msg,
                                                &current_connnect_id,//直接传地址
                                                1024,
                                                12,//优先级比原来的高
                                                20);
            if (tid != RT_NULL)
            {
                rt_thread_startup(tid);
            }
            current_connnect_id = (current_connnect_id + 1) % MAX_CONNECT_NUM;
            total_connect_count ++;
        }
        else
        {
            LOG_I("max num over\r\n");
            rt_thread_delay(100);
        }
    }
    __exit:
        return;
}

static void usage(void)
{
    rt_kprintf("Usage: tcpserver -p <port>\n");
    rt_kprintf("       tcpserver --stop\n");
    rt_kprintf("       tcpserver --help\n");
    rt_kprintf("\n");
    rt_kprintf("Miscellaneous:\n");
    rt_kprintf("  -p           Specify the host port number\n");
    rt_kprintf("  --stop       Stop tcpserver program\n");
    rt_kprintf("  --help       Print help information\n");
}

static void tcpserver_test(int argc, char** argv)
{
    rt_thread_t tid;

    if (argc == 1 || argc > 3)
    {
        LOG_I("Please check the command you entered!\n");
        goto __usage;
    }
    else
    {
        if (rt_strcmp(argv[1], "--help") == 0)
        {
            goto __usage;
        }
        else if (rt_strcmp(argv[1], "--stop") == 0)
        {
            is_running = 0;
            return;
        }
        else if (rt_strcmp(argv[1], "-p") == 0)
        {
            if (started)
            {
                LOG_I("The tcpclient has started!");
                LOG_I("Please stop tcpclient firstly, by: tcpclient --stop");
                return;
            }

            port = atoi(argv[2]);
        }
        else
        {
            goto __usage;
        }
    }

    tid = rt_thread_create("tcp_serv",
        tcpserv,
        RT_NULL,
        2048,
        RT_THREAD_PRIORITY_MAX - 1, 
        20);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }
    return;

__usage:
    usage();
}

#ifdef RT_USING_FINSH
MSH_CMD_EXPORT_ALIAS(tcpserver_test, tcpserver,
    Start a tcp server. Help: tcpserver --help);
#endif

测试步骤如下:

1.烧写代码,重启后,通过串口启动TCP服务器

2.打开TCP客户端工具,连接TCP服务器

3.客户端发送消息到服务器

 

第二种方法实现:Python 实现多个客户端连接的TCP服务器

使用python实现,就不需要这么麻烦了。直接导入socket模块即可

具体代码如下:

# -*- coding: UTF-8 -*-

import socket #导入socket包


def tcp_server_create(port):
    print("tcp_server")
    tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 绑定端口
    tcp_server.bind(("",port))
    tcp_server.listen(128)
    client_addr_list = []
    tcp_server.setblocking(False)
    while True:
        try:
            new_client,new_client_addr = tcp_server.accept()
            #new_client_addr[0] = ip
            #new_client_addr[1] = port
            print("Connected with %s:%s" % (new_client_addr[0],str(new_client_addr[1])))
        except Exception as Ex:
            pass
        else:
            new_client.setblocking(False)
            client_addr_list.append(new_client)#将连接添加到列表中
        
        for client_id in client_addr_list:
            try:
                new_client_content=client_id.recv(1024)
            except Exception as Ex:
                pass
            else:
                if new_client_content:
                    print("tcp cleint send data:%s" % (new_client_content))
                    client_id.send(str("hello,the message from tcp_server").encode("utf-8"))
                else:
                    client_addr_list.remove(client_id)
                    client_id.close()
            
            
def main():
    tcp_server_create(2222)
    
if __name__ == "__main__":
    main()

测试结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值