首页 > 技术文章 > LWIP_STM32_ENC28J60_NETCONN_TCP_SERVICER(5)

dengxiaojun 2015-04-18 17:10 原文

    前面说了TCP客户端通讯,这一篇来说说单片机作为服务器的通讯方法

    tcp客户端和服务器的链接做大的不同在于服务器是不需要主动链接谁的,他只需要绑定在自己得一个特定的端口之上,等别人来连接就好了,先创建任务

    

//创建TCP服务器线程
//返回值:0 TCP服务器创建成功
//        其他 TCP服务器创建失败
INT8U tcp_server_init(void)
{
    INT8U res;
    OS_CPU_SR cpu_sr;
    
    OS_ENTER_CRITICAL();    //关中断
    res = OSTaskCreate(tcp_server_thread,(void*)0,(OS_STK*)&TCPSERVER_TASK_STK[TCPSERVER_STK_SIZE-1],TCPSERVER_PRIO); //创建TCP服务器线程
    OS_EXIT_CRITICAL();        //开中断
    
    return res;
}

任务循环如下

//tcp服务器任务
static void tcp_server_thread(void *arg)
{
    OS_CPU_SR cpu_sr;
    u32 data_len = 0;
    struct pbuf *q;
    err_t err,recv_err;
    u8 remot_addr[4];
    struct netconn *conn, *newconn;
    static ip_addr_t ipaddr;
    static u16_t             port;
    struct netbuf *recvbuf;
    
    while(dhcpstatus != 2)//等待dhcp成功
    {
        OSTimeDly(10);
        //printf("wait dhcp\r\n");
    }
    
    LWIP_UNUSED_ARG(arg);
    printf("创建一个TCP链接\r\n");
    conn = netconn_new(NETCONN_TCP);              //创建一个TCP链接
    netconn_bind(conn,IP_ADDR_ANY,TCP_SERVER_PORT);  //绑定端口 8088号端口
    netconn_listen(conn);                              //进入监听模式
    printf("进入监听模式");
    //这个地方阻塞之后会造成客户端连不上,报错为the socket is marked as non blocking,后面研究
//    conn->recv_timeout = 10;  //禁止阻塞线程 等待10ms
    while (1) 
    {
        err = netconn_accept(conn,&newconn);  //接收连接请求
        newconn->recv_timeout = 10;

        if (err == ERR_OK)    //处理新连接的数据
        { 
            printf("处理新连接的数据");
            
            netconn_getaddr(newconn,&ipaddr,&port,0); //获取远端IP地址和端口号
            
            remot_addr[3] = (uint8_t)(ipaddr.addr >> 24); 
            remot_addr[2] = (uint8_t)(ipaddr.addr>> 16);
            remot_addr[1] = (uint8_t)(ipaddr.addr >> 8);
            remot_addr[0] = (uint8_t)(ipaddr.addr);
            printf("主机%d.%d.%d.%d连接上服务器,主机端口号为:%d\r\n",\
            remot_addr[0], remot_addr[1],remot_addr[2],remot_addr[3],port);
            
            while(1)
            {
                if(keyValue == KEY_UP)
                {
                    keyValue = 0;
                    tcp_server_flag = LWIP_SEND_DATA;
                }
                if((tcp_server_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) //有数据要发送
                {
                    err = netconn_write(newconn ,tcp_server_sendbuf,strlen((char*)tcp_server_sendbuf),NETCONN_COPY); //发送tcp_server_sendbuf中的数据
                    if(err != ERR_OK)
                    {
                        printf("发送失败\r\n");
                    }
                    tcp_server_flag &= ~LWIP_SEND_DATA;
                }
                
                if((recv_err = netconn_recv(newconn,&recvbuf)) == ERR_OK)      //接收到数据
                {        
                    OS_ENTER_CRITICAL(); //关中断
                    memset(tcp_server_recvbuf,0,TCP_SERVER_RX_BUFSIZE);  //数据接收缓冲区清零
                    for(q=recvbuf->p;q!=NULL;q=q->next)  //遍历完整个pbuf链表
                    {
                        //判断要拷贝到TCP_SERVER_RX_BUFSIZE中的数据是否大于TCP_SERVER_RX_BUFSIZE的剩余空间,如果大于
                        //的话就只拷贝TCP_SERVER_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
                        if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len)) memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));//拷贝数据
                        else memcpy(tcp_server_recvbuf+data_len,q->payload,q->len);
                        data_len += q->len;      
                        if(data_len > TCP_SERVER_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出    
                    }
                    OS_EXIT_CRITICAL();  //开中断
                    data_len=0;  //复制完成后data_len要清零。    
                    printf("%s\r\n",tcp_server_recvbuf);  //通过串口发送接收到的数据
                    netbuf_delete(recvbuf);
                }else if(recv_err == ERR_CLSD)  //关闭连接
                {
                    netconn_close(newconn);
                    netconn_delete(newconn);
                    printf("主机:%d.%d.%d.%d断开与服务器的连接\r\n",remot_addr[0], remot_addr[1],remot_addr[2],remot_addr[3]);
                    break;
                }
            }
            OSTimeDly(10);
        }
        OSTimeDly(10);
    }
}

这里面用了两个服务器特有的api,如下

netconn_bind(conn,IP_ADDR_ANY,TCP_SERVER_PORT);  //绑定端口 8088号端口
    netconn_listen(conn);                              //进入监听模式

其实严格来说,监听之后应该来一个任务生成一个新的任务处理相应的链接,这个demo里面没这个做,循序渐进嘛

代码位置如下

http://download.csdn.net/detail/dengrengong/8599075

 

推荐阅读