lwip設置recv接收函數超時時間

在socket編程中,經常使用recv函數阻塞等待接收數據。
如果對方GG了(接收到你的數據之後並沒有返回,你這裏會一直等待下去),顯然我們是不希望出現這種情況的。
一般情況下,考慮到對方的數據處理時間,我們可以設置一個超時時間,比如10s,10s之後如果對方還沒返回消息,我們就應該做相應的處理。
核心代碼:

	struct timeval tv_out;
    tv_out.tv_sec = 5;
    tv_out.tv_usec = 0;
    setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

下方是一個demo,其中使用了ulog組件可以方便的查看日誌時間,我們把超時時間設置的5s,可以看到server監聽後,socket號爲1的客戶端連接上了,我們設置5s超時時間,開始接收:
(1)一直沒有數據發過來,5s時間之後,我們recv超時返回-1,接下來我們進行相應操作,主動斷開這個連接;
(2)如果有數據發過來,recv返回數據長度,進行正確數據處理後繼續recv,此時又重新開始5s倒計時;
(3)如果等待過程中客戶端主動關閉socket連接,那麼recv返回0,進行退出操作。
在這裏插入圖片描述

/*
 * Copyright (c) 2006-2019, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-02-20     ShineRoyal   the first version
 */

#include <sys/socket.h>
#include <netdev.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>


#define DBG_TAG "tcpserver"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>


static const char *send_data = "hello RT-Thread\n";

struct client_info
{
    int socketnum;              //socket 號
    struct sockaddr_in addr;    //socket客戶端的ip和port信息
    int sockaddrlen;            //socketaddr的長度信息
};

void client_thread_entry(void *param)
{
    struct client_info* client = param;
    LOG_D("[%d]%s:%d is connect...", client->socketnum, inet_ntoa(client->addr.sin_addr),
            ntohs(client->addr.sin_port));
    send(client->socketnum, (const void* )send_data, strlen(send_data), 0);

    struct timeval tv_out;
    tv_out.tv_sec = 5;
    tv_out.tv_usec = 0;
    setsockopt(client->socketnum, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));
    LOG_D("[%d]set timeout %d.%03ds",client->socketnum, tv_out.tv_sec, tv_out.tv_usec);
    while (1)
    {
        char str[100];
        rt_memset(str, 0, sizeof(str));
        int bytes = recv(client->socketnum, str, sizeof(str), 0);
        LOG_D("bytes:%d", bytes);
        if (bytes == 0)
            goto __exit;
        else if (bytes == -1)
            goto __error;
        LOG_D("[%d]%s:%d=>%s...", client->socketnum, inet_ntoa(client->addr.sin_addr),
                ntohs(client->addr.sin_port), str);
        send((int )client->socketnum, (const void * )str, (size_t )strlen(str), 0);
    }
    __exit: LOG_D("[%d]%s:%d is disconnect...", client->socketnum, inet_ntoa(client->addr.sin_addr),
            ntohs(client->addr.sin_port));
    rt_free(client);
    closesocket(client->socketnum);
    return;

    __error: LOG_D("[%d]%s:%d is error...", client->socketnum, inet_ntoa(client->addr.sin_addr),
            ntohs(client->addr.sin_port));
    rt_free(client);
    closesocket(client->socketnum);
    return;

}

void tcpserver(int argc, char **argv)
{
    rt_thread_t tid = RT_NULL;
    int sock_listen, sock_connect, port;
    struct hostent *host;
    struct sockaddr_in listen_addr;
    struct sockaddr_in connect_addr;
    const char *url;

    url = "192.168.1.42";   //localhost ip
    port = 5000;
    /* 通過函數入口參數 url 獲得 host 地址(如果是域名,會做域名解析) */
    host = (struct hostent *) gethostbyname(url);

    if ((sock_listen = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        LOG_D("Socket error");
        return;
    }

    struct netdev *netdev = RT_NULL;

    netdev = netdev_get_by_family(AF_INET);
    if (netdev == RT_NULL)
    {
        LOG_D("get network interface device by AF_INET failed.");
    }

    LOG_D("localip:%s", inet_ntoa(netdev->ip_addr));
    /* 初始化預連接的服務端地址 */
    listen_addr.sin_family = AF_INET;
    listen_addr.sin_port = htons(port);
    listen_addr.sin_addr = *((struct in_addr *) host->h_addr);
    listen_addr.sin_addr.s_addr = netdev->ip_addr.addr;

    rt_memset(&(listen_addr.sin_zero), 0, sizeof(listen_addr.sin_zero));

    if (bind(sock_listen, (struct sockaddr * )&listen_addr, sizeof(struct sockaddr)) < 0)
    {
        LOG_D("Bind fail!");
        goto __exit;
    }

    listen(sock_listen, 3);
    LOG_D("begin listing...");

    while (1)
    {
        int sin_size = sizeof(struct sockaddr_in);
        sock_connect = accept(sock_listen, (struct sockaddr* )&connect_addr, (socklen_t* )&sin_size);
        if (sock_connect == -1)
        {
            LOG_D("no socket,waitting others socket disconnect.");
            continue;
        }

        char tid_name[10] = "cli";
        char tid_num[10];
        itoa(sock_connect, tid_num, 10);
        strcat(tid_name, tid_num);

        struct client_info *client;
        client = rt_malloc(sizeof(struct client_info));
        client->socketnum = sock_connect;
        rt_memcpy(&client->addr, &connect_addr, sizeof(struct sockaddr_in));
        client->sockaddrlen = sin_size;
        tid = rt_thread_create(tid_name, client_thread_entry, (void*) client, 4096, 25, 10);
        if (tid == RT_NULL)
        {
            LOG_D("no memery for thread %s startup failed!", tid_name);
            rt_free(client);
            continue;
        }
        rt_thread_startup(tid);
    }
    __exit: LOG_D("close listener...");
    /* 關閉這個 socket */
    closesocket(sock_listen);
    return;
}
MSH_CMD_EXPORT(tcpserver, tcpserver);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章