Skip to content

RAW接口

image-20240712175034919

但是这一个使用的时候代码的可读性比较差, 并且逻辑比较复杂

UDP

API

image-20240712175130749

c
/**
 * @ingroup udp_raw
 * Creates a new UDP pcb which can be used for UDP communication. The
 * pcb is not active until it has either been bound to a local address
 * or connected to a remote address.
 * @see MEMP_NUM_UDP_PCB
 *
 * @return The UDP PCB which was created. NULL if the PCB data structure
 * could not be allocated.
 *
 * @see udp_remove()
 */
struct udp_pcb * udp_new(void);
c
/**
 * @ingroup udp_raw
 * Removes and deallocates the pcb.  
 * 
 * @param pcb UDP PCB to be removed. The PCB is removed from the list of
 * UDP PCB's and the data structure is freed from memory.
 *
 * @see udp_new()
 */
void udp_remove(struct udp_pcb *pcb);
c
/**
 * @ingroup udp_raw
 * Bind an UDP PCB.
 * 
 * @param pcb UDP PCB to be bound with a local address ipaddr and port.
 * @param ipaddr local IP address to bind with. Use IP_ANY_TYPE to
 * bind to all local interfaces.
 * @param port local UDP port to bind with. Use 0 to automatically bind
 * to a random port between UDP_LOCAL_PORT_RANGE_START and
 * UDP_LOCAL_PORT_RANGE_END.
 *
 * ipaddr & port are expected to be in the same byte order as in the pcb.
 *
 * @return lwIP error code.
 * - ERR_OK. Successful. No error occurred.
 * - ERR_USE. The specified ipaddr and port are already bound to by
 * another UDP PCB.
 *
 * @see udp_disconnect()
 */
err_t udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)

之后会使用这一个端口号进行获取数据

c
/**
 * @ingroup udp_raw
 * Sets the remote end of the pcb. This function does not generate any
 * network traffic, but only sets the remote address of the pcb.
 *
 * @param pcb UDP PCB to be connected with remote address ipaddr and port.
 * @param ipaddr remote IP address to connect with.
 * @param port remote UDP port to connect with.
 *
 * @return lwIP error code
 *
 * ipaddr & port are expected to be in the same byte order as in the pcb.
 *
 * The udp pcb is bound to a random local port if not already bound.
 *
 * @see udp_disconnect()
 */
err_t udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port);

进行一个远程的连接, 连接以后可以发送数据

c
/**
 * @ingroup udp_raw
 * Remove the remote end of the pcb. This function does not generate
 * any network traffic, but only removes the remote address of the pcb.
 *
 * @param pcb the udp pcb to disconnect.
 */
void udp_disconnect(struct udp_pcb *pcb);
c
/**
 * @ingroup udp_raw
 * Sends the pbuf p using UDP. The pbuf is not deallocated.
 *
 *
 * @param pcb UDP PCB used to send the data.
 * @param p chain of pbuf's to be sent.
 *
 * The datagram will be sent to the current remote_ip & remote_port
 * stored in pcb. If the pcb is not bound to a port, it will
 * automatically be bound to a random port.
 *
 * @return lwIP error code.
 * - ERR_OK. Successful. No error occurred.
 * - ERR_MEM. Out of memory.
 * - ERR_RTE. Could not find route to destination address.
 * - ERR_VAL. No PCB or PCB is dual-stack
 * - More errors could be returned by lower protocol layers.
 *
 * @see udp_disconnect() udp_sendto()
 */
err_t
udp_send(struct udp_pcb *pcb, struct pbuf *p)

发送数据, 发送的时候使用pbuf

c
/**
 * @ingroup udp_raw
 * Set a receive callback for a UDP PCB.
 * This callback will be called when receiving a datagram for the pcb.
 *
 * @param pcb the pcb for which to set the recv callback
 * @param recv function pointer of the callback function
 * @param recv_arg additional argument to pass to the callback function
 */
void
udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg);

注册一个接收使用的函数

c
/**
 * @brief       UDP服务器回调函数
 * @param       arg :传入参数
 * @param       upcb:UDP控制块
 * @param       p   : 网络数据包
 * @param       addr:IP地址
 * @param       port:端口号
 * @retval
 */
static void lwip_udp_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);

这一个函数里面的pbuf可以获取接收的数据

实际使用

c
udppcb = udp_new();

if (udppcb) /* 创建成功 */
{
    IP4_ADDR(&rmtipaddr, g_lwipdev.remoteip[0], g_lwipdev.remoteip[1], g_lwipdev.remoteip[2], g_lwipdev.remoteip[3]);
    err = udp_connect(udppcb, &rmtipaddr, LWIP_DEMO_PORT);      /* UDP客户端连接到指定IP地址和端口号的服务器 */

    if (err == ERR_OK)
    {
        err = udp_bind(udppcb, IP_ADDR_ANY, LWIP_DEMO_PORT);    /* 绑定本地IP地址与端口号 */

        if (err == ERR_OK)  /* 绑定完成 */
        {
            udp_recv(udppcb,lwip_udp_callback, NULL);           /* 注册接收回调函数 */
        }
        else res = 1;
    }
    else res = 1;
}
else res = 1;

TCP

image-20240712191144150

实际使用的时候需要初始化这些回调函数

image-20240712191331506

image-20240712191353958

API

建立连接

c
/**
 * @ingroup tcp_raw
 * Creates a new TCP protocol control block but doesn't place it on
 * any of the TCP PCB lists.
 * The pcb is not put on any list until binding using tcp_bind().
 * If memory is not available for creating the new pcb, NULL is returned.
 * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
 *
 * @internal: Maybe there should be a idle TCP PCB list where these
 * PCBs are put on. Port reservation using tcp_bind() is implemented but
 * allocated pcbs that are not bound can't be killed automatically if wanting
 * to allocate a pcb with higher prio (@see tcp_kill_prio())
 *
 * @return a new tcp_pcb that initially is in state CLOSED
 */
struct tcp_pcb * tcp_new(void);
c
/**
 * @ingroup tcp_raw
 * Binds the connection to a local port number and IP address. If the
 * IP address is not given (i.e., ipaddr == IP_ANY_TYPE), the connection is
 * bound to all local IP addresses.
 * If another connection is bound to the same port, the function will
 * return ERR_USE, otherwise ERR_OK is returned.
 * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
 *
 * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
 *        already bound!)
 * @param ipaddr the local ip address to bind to (use IPx_ADDR_ANY to bind
 *        to any local address
 * @param port the local port to bind to
 * @return ERR_USE if the port is already in use
 *         ERR_VAL if bind failed because the PCB is not in a valid state
 *         ERR_OK if bound
 */
err_t tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)

绑定一个本地端口, 用于进行连接

c
#define          tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
/**
 * @ingroup tcp_raw
 * Set the state of the connection to be LISTEN, which means that it
 * is able to accept incoming connections. The protocol control block
 * is reallocated in order to consume less memory. Setting the
 * connection to LISTEN is an irreversible process.
 * When an incoming connection is accepted, the function specified with
 * the tcp_accept() function will be called. The pcb has to be bound
 * to a local port with the tcp_bind() function.
 * 
 * The tcp_listen() function returns a new connection identifier, and
 * the one passed as an argument to the function will be
 * deallocated. The reason for this behavior is that less memory is
 * needed for a connection that is listening, so tcp_listen() will
 * reclaim the memory needed for the original connection and allocate a
 * new smaller memory block for the listening connection.
 *
 * tcp_listen() may return NULL if no memory was available for the
 * listening connection. If so, the memory associated with the pcb
 * passed as an argument to tcp_listen() will not be deallocated.
 *
 * The backlog limits the number of outstanding connections
 * in the listen queue to the value specified by the backlog argument.
 * To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
 * 
 * @param pcb the original tcp_pcb
 * @param backlog the incoming connections queue limit
 * @return tcp_pcb used for listening, consumes less memory.
 *
 * @note The original tcp_pcb is freed. This function therefore has to be
 *       called like this:
 *             tpcb = tcp_listen_with_backlog(tpcb, backlog);
 */
struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)

实际限制了可以同时连接的个数, 默认是0xff最大值

c
/**
 * @ingroup tcp_raw
 * Used for specifying the function that should be called when a
 * LISTENing connection has been connected to another host.
 * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
 *
 * @param pcb tcp_pcb to set the accept callback
 * @param accept callback function to call for this pcb when LISTENing
 *        connection has been connected to another host
 */
void
tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept);
/** Function prototype for tcp accept callback functions. Called when a new
 * connection can be accepted on a listening pcb.
 *
 * @param arg Additional argument to pass to the callback function (@see tcp_arg())
 * @param newpcb The new connection pcb
 * @param err An error code if there has been an error accepting.
 *            Only return ERR_ABRT if you have called tcp_abort from within the
 *            callback function!
 */
typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err);

使用这一个函数设置一个连接的时候回调函数

c
/**
 * @ingroup tcp_raw
 * Connects to another host. The function given as the "connected"
 * argument will be called when the connection has been established.
 *  Sets up the pcb to connect to the remote host and sends the
 * initial SYN segment which opens the connection. 
 *
 * The tcp_connect() function returns immediately; it does not wait for
 * the connection to be properly setup. Instead, it will call the
 * function specified as the fourth argument (the "connected" argument)
 * when the connection is established. If the connection could not be
 * properly established, either because the other host refused the
 * connection or because the other host didn't answer, the "err"
 * callback function of this pcb (registered with tcp_err, see below)
 * will be called.
 *
 * The tcp_connect() function can return ERR_MEM if no memory is
 * available for enqueueing the SYN segment. If the SYN indeed was
 * enqueued successfully, the tcp_connect() function returns ERR_OK.
 *
 * @param pcb the tcp_pcb used to establish the connection
 * @param ipaddr the remote ip address to connect to
 * @param port the remote tcp port to connect to
 * @param connected callback function to call when connected (on error,
                    the err calback will be called)
 * @return ERR_VAL if invalid arguments are given
 *         ERR_OK if connect request has been sent
 *         other err_t values if connect request couldn't be sent
 */
err_t
tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
            tcp_connected_fn connected);
/** Function prototype for tcp connected callback functions. Called when a pcb
 * is connected to the remote side after initiating a connection attempt by
 * calling tcp_connect().
 *
 * @param arg Additional argument to pass to the callback function (@see tcp_arg())
 * @param tpcb The connection pcb which is connected
 * @param err An unused error code, always ERR_OK currently ;-) @todo!
 *            Only return ERR_ABRT if you have called tcp_abort from within the
 *            callback function!
 *
 * @note When a connection attempt fails, the error callback is currently called!
 */
typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err);

连接另一个设备, 使用IP地址和对方的端口, 还需要一个连接成功的回调函数

发送数据

c
/**
 * @ingroup tcp_raw
 * Write data for sending (but does not send it immediately).
 *
 * It waits in the expectation of more data being sent soon (as
 * it can send them more efficiently by combining them together).
 * To prompt the system to send data now, call tcp_output() after
 * calling tcp_write().
 * 
 * This function enqueues the data pointed to by the argument dataptr. The length of
 * the data is passed as the len parameter. The apiflags can be one or more of:
 * - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
 *   for the data to be copied into. If this flag is not given, no new memory
 *   should be allocated and the data should only be referenced by pointer. This
 *   also means that the memory behind dataptr must not change until the data is
 *   ACKed by the remote host
 * - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is omitted,
 *   the PSH flag is set in the last segment created by this call to tcp_write.
 *   If this flag is given, the PSH flag is not set.
 *
 * The tcp_write() function will fail and return ERR_MEM if the length
 * of the data exceeds the current send buffer size or if the length of
 * the queue of outgoing segment is larger than the upper limit defined
 * in lwipopts.h. The number of bytes available in the output queue can
 * be retrieved with the tcp_sndbuf() function.
 *
 * The proper way to use this function is to call the function with at
 * most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
 * the application should wait until some of the currently enqueued
 * data has been successfully received by the other host and try again.
 *
 * @param pcb Protocol control block for the TCP connection to enqueue data for.
 * @param arg Pointer to the data to be enqueued for sending.
 * @param len Data length in bytes
 * @param apiflags combination of following flags :
 * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
 * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent,
 * @return ERR_OK if enqueued, another err_t on error
 */
err_t tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)

发送数据但是不是立即发送, 可以把多个数据进行合并

c
/**
 * @ingroup tcp_raw
 * Specifies the callback function that should be called when data has
 * successfully been received (i.e., acknowledged) by the remote
 * host. The len argument passed to the callback function gives the
 * amount bytes that was acknowledged by the last acknowledgment.
 *
 * @param pcb tcp_pcb to set the sent callback
 * @param sent callback function to call for this pcb when data is successfully sent
 */
void tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)

设置发送结束的回调函数

c
/**
 * @ingroup tcp_raw
 * Find out what we can send and send it
 *
 * @param pcb Protocol control block for the TCP connection to send data
 * @return ERR_OK if data has been sent or nothing to send
 *         another err_t on error
 */
err_t tcp_output(struct tcp_pcb *pcb)

把缓冲区里面的数据发送出去

接收数据

c
/**
 * @ingroup tcp_raw
 * Sets the callback function that will be called when new data
 * arrives. The callback function will be passed a NULL pbuf to
 * indicate that the remote host has closed the connection. If the
 * callback function returns ERR_OK or ERR_ABRT it must have
 * freed the pbuf, otherwise it must not have freed it.
 *
 * @param pcb tcp_pcb to set the recv callback
 * @param recv callback function to call for this pcb when data is received
 */
void
tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)

设置接收数据的回调函数

c
/**
 * @ingroup tcp_raw
 * This function should be called by the application when it has
 * processed the data. The purpose is to advertise a larger window
 * when the data has been processed.
 *
 * @param pcb the tcp_pcb for which data is read
 * @param len the amount of bytes that have been read by the application
 */
void
tcp_recved(struct tcp_pcb *pcb, u16_t len)

断开

c
/**
 * @ingroup tcp_raw
 * Closes the connection held by the PCB.
 *
 * Listening pcbs are freed and may not be referenced any more.
 * Connection pcbs are freed if not yet connected and may not be referenced
 * any more. If a connection is established (at least SYN received or in
 * a closing state), the connection is closed, and put in a closing state.
 * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
 * unsafe to reference it (unless an error is returned).
 * 
 * The function may return ERR_MEM if no memory
 * was available for closing the connection. If so, the application
 * should wait and try again either by using the acknowledgment
 * callback or the polling functionality. If the close succeeds, the
 * function returns ERR_OK.
 *
 * @param pcb the tcp_pcb to close
 * @return ERR_OK if connection has been closed
 *         another err_t if closing failed and pcb is not freed
 */
err_t tcp_close(struct tcp_pcb *pcb)

断开链接, 这一个pcb不再进行连接, 并释放已经进行的连接和这一个控制块, 用于监听pcb

c
/**
 * @ingroup tcp_raw
 * Aborts the connection by sending a RST (reset) segment to the remote
 * host. The pcb is deallocated. This function never fails.
 *
 * ATTENTION: When calling this from one of the TCP callbacks, make
 * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
 * or you will risk accessing deallocated memory or memory leaks!
 *
 * @param pcb the tcp pcb to abort
 */
void tcp_abort(struct tcp_pcb *pcb);

一个普通的pcb断开连接, 在回调函数里面调用的时候, 回调函数需要返回一个ERR_ABRT

使用

客户端

c
tcppcb = tcp_new();  /* 创建一个新的pcb */

if (tcppcb)           /* 创建成功 */
{
    IP4_ADDR(&rmtipaddr, g_lwipdev.remoteip[0], g_lwipdev.remoteip[1], g_lwipdev.remoteip[2], g_lwipdev.remoteip[3]);
    tcp_connect(tcppcb, &rmtipaddr, LWIP_DEMO_PORT, lwip_tcp_client_connected); /* 连接到目的地址的指定端口上,当连接成功后回调lwip_tcp_client_connected()函数 */
}
else res = 1;

使用这一个方式进行连接

  • 连接回调函数
c
/**
 * @brief       lwIP TCP连接建立后调用回调函数
 * @param       arg   : 回调函数传入的参数
 * @param       tpcb  : TCP控制块
 * @param       err   : 错误码
 * @retval      返回错误码
 */
err_t lwip_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
    struct tcp_client_struct *es = NULL;

    if (err == ERR_OK)
    {
        es = (struct tcp_client_struct *)mem_malloc(sizeof(struct tcp_client_struct)); /* 申请内存 */

        if (es) /* 内存申请成功 */
        {
            es->state = ES_TCPCLIENT_CONNECTED;         /* 状态为连接成功 */
            es->pcb = tpcb;
            es->p = NULL;
            tcp_arg(tpcb, es);                          /* 使用es更新tpcb的callback_arg */
            tcp_recv(tpcb, lwip_tcp_client_recv);       /* 初始化LwIP的tcp_recv回调功能 */
            tcp_err(tpcb, lwip_tcp_client_error);       /* 初始化tcp_err()回调函数 */
            tcp_sent(tpcb, lwip_tcp_client_sent);       /* 初始化LwIP的tcp_sent回调功能 */
            tcp_poll(tpcb, lwip_tcp_client_poll, 1);    /* 初始化LwIP的tcp_poll回调功能 */
            g_lwip_send_flag |= 1 << 5;                 /* 标记连接到服务器了 */
            err = ERR_OK;
        }
        else
        {
            lwip_tcp_client_connection_close(tpcb, es); /* 关闭连接 */
            err = ERR_MEM;                              /* 返回内存分配错误 */
        }
    }
    else
    {
        lwip_tcp_client_connection_close(tpcb, 0);      /* 关闭连接 实际是设置回调函数为NULL*/
    }

    return err;
}
  • 断开连接
c
/**
 * @brief       关闭与服务器的连接
 * @param       tpcb: TCP控制块
 * @param       es  : LWIP回调函数使用的结构体
 * @retval
 */
void lwip_tcp_client_connection_close(struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{
    /* 移除回调 */
    tcp_abort(tpcb);                        /* 终止连接,删除pcb控制块 */
    tcp_arg(tpcb, NULL);
    tcp_recv(tpcb, NULL);
    tcp_sent(tpcb, NULL);
    tcp_err(tpcb, NULL);
    tcp_poll(tpcb, NULL, 0);

    if (es)mem_free(es);

    g_lwip_send_flag &= ~(1 << 5);          /* 标记连接断开了 */
}
  • 轮询函数
c
/**
 * @brief       lwIP tcp_poll的回调函数
 * @param       arg : 回调函数传入的参数
 * @param       tpcb: TCP控制块
 * @retval      ERR_OK
 */
err_t lwip_tcp_client_poll(void *arg, struct tcp_pcb *tpcb)
{
    err_t ret_err;
    struct tcp_client_struct *es;
    es = (struct tcp_client_struct *)arg;

    if (es->state == ES_TCPCLIENT_CLOSING)              /* 连接断开 */
    {
        lwip_tcp_client_connection_close(tpcb, es);     /* 关闭TCP连接 */
    }

    ret_err = ERR_OK;
    return ret_err;
}

循环的回调函数, 这一个函数会被循环调用

  • 发送函数
c
/**
 * @brief       lwIP tcp_sent的回调函数(当从远端主机接收到ACK信号后发送数据)
 * @param       arg : 回调函数传入的参数
 * @param       tpcb: TCP控制块
 * @param       len : 长度
 * @retval      ERR_OK
 */
err_t lwip_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
    struct tcp_client_struct *es;
    LWIP_UNUSED_ARG(len);
    es = (struct tcp_client_struct *)arg;

    if (es->p)lwip_tcp_client_senddata(tpcb, es); /* 发送数据 */

    return ERR_OK;
}

/**
 * @brief       用来发送数据
 * @param       tpcb: TCP控制块
 * @param       es  : LWIP回调函数使用的结构体
 * @retval
 */
void lwip_tcp_client_senddata(struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{
    struct pbuf *ptr;
    err_t wr_err = ERR_OK;

    while ((wr_err == ERR_OK) && es->p && (es->p->len <= tcp_sndbuf(tpcb))) /* 将要发送的数据加入到发送缓冲队列中 */
    {
        ptr = es->p;
        wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1); //把数据加入缓冲里面

        if (wr_err == ERR_OK)
        {
            es->p = ptr->next;             /* 指向下一个pbuf */

            if (es->p)pbuf_ref(es->p);     /* pbuf的ref加一 */

            pbuf_free(ptr);                /* 释放ptr */
        }
        else if (wr_err == ERR_MEM)es->p = ptr;

        tcp_output(tpcb);                  /* 将发送缓冲队列中的数据立即发送出去 */
    }
}

实际发送的时候, 可以直接调用lwip_tcp_client_senddata函数进行发送

  • 接收函数
c
/**
 * @brief       lwIP tcp_recv()函数的回调函数
 * @param       arg   : 回调函数传入的参数
 * @param       tpcb  : TCP控制块
 * @param       p     : 网络数据包
 * @param       err   : 错误码
 * @retval      返回错误码
 */
err_t lwip_tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    uint32_t data_len = 0;
    struct pbuf *q;
    struct tcp_client_struct *es;
    err_t ret_err;
    LWIP_ASSERT("arg != NULL", arg != NULL);
    es = (struct tcp_client_struct *)arg;

    if (p == NULL)                                                  /* 如果从服务器接收到空的数据帧就关闭连接 */
    {
        es->state = ES_TCPCLIENT_CLOSING;                           /* 需要关闭TCP 连接了 */
        es->p = p;
        ret_err = ERR_OK;
    }
    else if (err != ERR_OK)                                         /* 当接收到一个非空的数据帧,但是err!=ERR_OK */
    {
        if (p)pbuf_free(p);                                         /* 释放接收pbuf */

        ret_err = err;
    }
    else if (es->state == ES_TCPCLIENT_CONNECTED)                   /* 当处于连接状态时 */
    {
        if (p != NULL)                                              /* 当处于连接状态并且接收到的数据不为空时 */
        {
            memset(g_lwip_demo_recvbuf, 0, LWIP_DEMO_RX_BUFSIZE);   /* 数据接收缓冲区清零 */

            for (q = p; q != NULL; q = q->next)                     /* 遍历完整个pbuf链表 */
            {
                /* 判断要拷贝到LWIP_DEMO_RX_BUFSIZE中的数据是否大于LWIP_DEMO_RX_BUFSIZE的剩余空间,如果大于 */
                /* 的话就只拷贝LWIP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据 */
                if (q->len > (LWIP_DEMO_RX_BUFSIZE - data_len)) memcpy(g_lwip_demo_recvbuf + data_len, q->payload, (LWIP_DEMO_RX_BUFSIZE - data_len)); /* 拷贝数据 */
                else memcpy(g_lwip_demo_recvbuf + data_len, q->payload, q->len);

                data_len += q->len;

                if (data_len > LWIP_DEMO_RX_BUFSIZE) break; /* 超出TCP客户端接收数组,跳出 */
            }

            g_lwip_send_flag |= 1 << 6;                     /* 标记接收到数据了 */
            tcp_recved(tpcb, p->tot_len);                   /* 用于获取接收数据,通知LWIP可以获取更多数据 */
            pbuf_free(p);                                   /* 释放内存 */
            ret_err = ERR_OK;
        }
    }
    else                                                    /* 接收到数据但是连接已经关闭 */
    {
        tcp_recved(tpcb, p->tot_len);                       /* 用于获取接收数据,通知LWIP可以获取更多数据 */
        es->p = NULL;
        pbuf_free(p);                                       /* 释放内存 */
        ret_err = ERR_OK;
    }

    return ret_err;
}

服务器

c
tcppcbnew = tcp_new();              /* 创建一个新的pcb */

if (tcppcbnew)                      /* 创建成功 */
{
    err = tcp_bind(tcppcbnew, IP_ADDR_ANY, LWIP_DEMO_PORT); /* 将本地IP与指定的端口号绑定在一起,IP_ADDR_ANY为绑定本地所有的IP地址 */

    if (err == ERR_OK)              /* 绑定完成 */
    {
        tcppcbconn = tcp_listen(tcppcbnew);                 /* 设置tcppcb进入监听状态 */
        tcp_accept(tcppcbconn, lwip_tcp_server_accept);     /* 初始化LWIP的tcp_accept的回调函数 */
    }
    else res = 1;
}
else res = 1;
c
/**
 * @brief       lwIP tcp_accept()的回调函数
 * @param       arg :传入的参数
 * @param       newpcb:TCP控制块
 * @param       err :错误码
 * @retval      返回 ret_err
 */
err_t lwip_tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    err_t ret_err;
    struct tcp_server_struct *es;
    LWIP_UNUSED_ARG(arg);
    LWIP_UNUSED_ARG(err);
    tcp_setprio(newpcb, TCP_PRIO_MIN);                                  /* 设置新创建的pcb优先级 */
    es = (struct tcp_server_struct *)mem_malloc(sizeof(struct tcp_server_struct)); /* 分配内存 */

    if (es != NULL)                                                     /* 内存分配成功 */
    {
        es->state = ES_TCPSERVER_ACCEPTED;                              /* 接收连接 */
        es->pcb = newpcb;
        es->p = NULL;
        
        tcp_arg(newpcb, es);
        tcp_recv(newpcb, lwip_tcp_server_recv);                         /* 初始化tcp_recv()的回调函数 */
        tcp_err(newpcb, lwip_tcp_server_error);                         /* 初始化tcp_err()回调函数 */
        tcp_poll(newpcb, lwip_tcp_server_poll, 1);                      /* 初始化tcp_poll回调函数 */
        tcp_sent(newpcb, lwip_tcp_server_sent);                         /* 初始化发送回调函数 */

        g_lwip_send_flag |= 1 << 5;                                     /* 标记有客户端连上了 */
        g_lwipdev.remoteip[0] = newpcb->remote_ip.addr & 0xff;          /* IADDR4 */
        g_lwipdev.remoteip[1] = (newpcb->remote_ip.addr >> 8) & 0xff;   /* IADDR3 */
        g_lwipdev.remoteip[2] = (newpcb->remote_ip.addr >> 16) & 0xff;  /* IADDR2 */
        g_lwipdev.remoteip[3] = (newpcb->remote_ip.addr >> 24) & 0xff;  /* IADDR1 */
        ret_err = ERR_OK;
    }
    else
    {
        ret_err = ERR_MEM;
    }

    return ret_err;
}