本人在做LM3S8962作为客户端测试时,遇到个问题,如果TCP测试软件建立服务器后,客户端有可能会等很久才连上来,关掉了这个连接,就再也建立不上与8962的通信。迷惑中,请高手帮帮忙,万分感谢!我的源码如下:
//-----------------------------------------------------------------------------
//
// enet_lwip_tcp.c - 一个简单的lwIP tcp通信例程
//
//-----------------------------------------------------------------------------
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
#include "driverlib/ethernet.h"
#include "driverlib/flash.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "utils/lwiplib.h"
#include "utils/uartstdio.h"
#include "utils/truly128x64.h"
#include "utils/ustdlib.h"
#include <LM3Sxxxx.h>
//----------------------------------------------------------------------------
//! 这个例子展示了对Stellaris以太网控制器的使用,通过lwIP TCP/IP 协议栈
// http://savannah.nongnu.org/projects/lwip/
//----------------------------------------------------------------------------
#define ulPORT 3000
//----------------------------------------------------------------------------
//
// Defines for setting up the system clock.
// 定义systick时钟
//-----------------------------------------------------------------------------
#define SYSTICKHZ 100
#define SYSTICKMS (1000 / SYSTICKHZ)
unsigned char sent_ok_flag = 0;
struct tcp_pcb *now_pcb;
char tmpbuf[20];
void *ptr;
struct ip_addr ipaddr;
//-----------------------------------------------------------------------------
//
// 显示lwip的IP地址信息函数
//
//-----------------------------------------------------------------------------
void
DisplayIPAddress(unsigned long ipaddr, unsigned long ulCol,
unsigned long ulRow)
{
char pucBuf[16];
unsigned char *pucTemp = (unsigned char *)&ipaddr;
//
// 转换IP地址到字符串
//
usprintf(pucBuf, "%d.%d.%d.%d", pucTemp[0], pucTemp[1], pucTemp[2],
pucTemp[3]);
//
//显示字符串
//
Oled_Dis_string(ulCol,ulRow,pucBuf);
}
//-----------------------------------------------------------------------------
//
// 被lwIp库使用,用来支持任何主机相关的定时功能
//
//-----------------------------------------------------------------------------
void
lwIPHostTimerHandler(void)
{
static unsigned long ulLastIPAddress = 0;
unsigned long ulIPAddress;
char pucBuf[16];
ulIPAddress = lwIPLocalIPAddrGet();
//
// If IP Address has not yet been assigned, update the display accordingly
//
if(ulIPAddress == 0)
{
}
//
// Check if IP address has changed, and display if it has.
//
else if(ulLastIPAddress != ulIPAddress)
{
ulLastIPAddress = ulIPAddress;
Oled_Dis_string(2,2,"IP: ");
Oled_Dis_string(3,2,"MASK: ");
Oled_Dis_string(4,2,"GW: ");
Oled_Dis_string(5,2,"Port: ");
DisplayIPAddress(ulIPAddress, 2, 38);
ulIPAddress = lwIPLocalNetMaskGet();
DisplayIPAddress(ulIPAddress, 3, 38);
ulIPAddress = lwIPLocalGWAddrGet();
DisplayIPAddress(ulIPAddress, 4, 38);
usprintf(pucBuf, "%d", ulPORT);
Oled_Dis_string(5, 38,pucBuf);
}
}
//-----------------------------------------------------------------------------
//
// systick中断处理函数
//
//-----------------------------------------------------------------------------
void
SysTickIntHandler(void)
{
//
// 调用lwip定时处理函数
//
lwIPTimer(SYSTICKMS);
}
//-----------------------------------------------------------------------------
//
// 发送完成回调函数
//
//-----------------------------------------------------------------------------
err_t Sent_Ok(void *arg,struct tcp_pcb *pcb,u16_t err)
{
tcp_close(pcb);
return ERR_OK;
}
//-----------------------------------------------------------------------------
//
// 接收到数据的回调函数
//
//-----------------------------------------------------------------------------
err_t recv_now(void *arg,struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
if(p != NULL) //如果收到的数据不为空
{
tcp_write(tpcb,p->payload,p->len,0); //将接收到的数据发送出去
pbuf_free(p); // 释放缓冲区数据
tcp_recved(tpcb,p->tot_len);
//tcp_output(tpcb);
}
return ERR_OK;
}
//-----------------------------------------------------------------------------
//
// 接受连接请求
//
//-----------------------------------------------------------------------------
err_t TCP_SER_Connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
//
// Clear out all of the TCP callbacks.
//
tcp_arg(pcb, NULL);
tcp_sent(pcb, NULL);
tcp_recv(pcb, NULL);
tcp_err(pcb, NULL);
tcp_poll(pcb, NULL, 1);
//
// Close the TCP connection.
//
tcp_close(pcb);
tcp_recv(pcb,recv_now);
return(ERR_OK);
}
/*****************************************************************************************
** Function name: TCP_SER_Poll
** Descriptions: 接收处理函数
** input parameters: 无
** output parameters: 无
** Returned value: 无
*****************************************************************************************/
err_t TCP_SER_Poll(void *arg, struct tcp_pcb *pcb)
{
err_t eError;
eError = tcp_connect(pcb, &ipaddr, ulPORT, TCP_SER_Connected);
if(eError != ERR_OK)
{
return 1;
}
return 0;
}
/*****************************************************************************************
** Function name: TCP_Client_Init
** Descriptions: 客户端模式初始化网络连接
** input parameters: ulIPAddr
** output parameters: 无
** Returned value: 无
*****************************************************************************************/
void TCP_Client_Init(unsigned long ulIPAddr)
{
struct tcp_pcb *local_pcb;
err_t eError;
ipaddr.addr = htonl(ulIPAddr);
local_pcb = tcp_new();
tcp_poll(local_pcb, TCP_SER_Poll, (3000 / TCP_SLOW_INTERVAL));
eError = tcp_connect(local_pcb, &ipaddr, ulPORT, TCP_SER_Connected);
if(eError != ERR_OK)
{
return;
}
}
//-----------------------------------------------------------------------------
//
// 使用以太网控制器的一个例子
//
//-----------------------------------------------------------------------------
int
main(void)
{
unsigned long ulUser0 = 0x12345678, ulUser1 = 0x12345678 ;
unsigned char pucMACArray[8];
unsigned long ulIPAddr,ulIPMask,ulIPGateWay,ulIPAddrR;
//
//设定系统时钟8MHZ
//
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_8MHZ);
//
// 初始化 OLED 显示.
//
Oled_Init();
Oled_Dis_string(1,10,"Enet lwip Tcp Test");
//
// 使能以太网控制器,然后复位以太网控制器
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH);
SysCtlPeripheralReset(SYSCTL_PERIPH_ETH);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
//
// 使能端口F来作为以太网的LED使用
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinConfigure(GPIO_PF2_LED1);
GPIOPinConfigure(GPIO_PF3_LED0);
GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); //使能Uart时钟
GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_0 | GPIO_PIN_1); //配置引脚为Rx、Tx功能
//
//配置UART0
//
UARTConfigSetExpClk(UART0_BASE,SysCtlClockGet(),9600,
UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
//配置为9600,8位,1个停止位,无校验
UARTIntEnable(UART0_BASE,UART_INT_RT | UART_INT_RX); //使能接收中断和接受超时中断
UARTEnable(UART0_BASE); //启动Uart
IntEnable(INT_UART0); //使能Uart中断
//
// 配置systick
//
SysTickPeriodSet(SysCtlClockGet() / SYSTICKHZ);
SysTickEnable();
SysTickIntEnable();
//
//使能处理器中断
//
IntMasterEnable();
//
//从用户寄存器中 读取MAC地址
//
//ROM_FlashUserGet(&ulUser0, &ulUser1);
if((ulUser0 == 0xffffffff) || (ulUser1 == 0xffffffff))
{
//
// MAC没有编程
//
Oled_Dis_string(5,5,"MAC Not Programmed!");
while(1)
{
}
}
//
//转换用户寄存器到MAC地址
//
pucMACArray[0] = ((ulUser0 >> 0) & 0xff);
pucMACArray[1] = ((ulUser0 >> 8) & 0xff);
pucMACArray[2] = ((ulUser0 >> 16) & 0xff);
pucMACArray[3] = ((ulUser1 >> 0) & 0xff);
pucMACArray[4] = ((ulUser1 >> 8) & 0xff);
pucMACArray[5] = ((ulUser1 >> 16) & 0xff);
//
// 初始化lwIP协议栈 静态IP
// IP地址: 192.168.14.20
// 子网掩码:255.255.255.0
// 默认网关:192.168.14.254
ulIPAddr = (((unsigned long )192<<24)|(168<<16)|(2<<8)|20);
ulIPMask = ((unsigned long )255<<24)|(255<<16)|(255<<8)|0;
ulIPGateWay = ((unsigned long )192<<24)|(168<<16)|(2<<8)|254;
ulIPAddrR = (((unsigned long )192<<24)|(168<<16)|(2<<8)|6);
lwIPInit(pucMACArray, ulIPAddr,
ulIPMask,
ulIPGateWay, IPADDR_USE_STATIC);
//
//初始化客户端TCP
//
TCP_Client_Init(ulIPAddrR);
//
// 主循环
//
while(1)
{
}
}
此帖出自
小平头技术问答
纯属胡说!!
RT-Thread核心开发、使用讨论
版主: bernard
发表回复4 篇帖子 • 分页: 1 / 1
搞死LWIP的方法
由 mbbill » 2010年 4月 18日 03:29
lwip的netconn有一个缺陷,它的tcp window和输入缓冲区没有直接联系,输入的数据是以pbuf的形式存放在conn->recvmbox里面的,这个mbox尺寸是10,这个size和缓冲区的大小无关,只是数据包的数量。
现在假设TCP_WND是1024,网络端快速发过来几十个1字节的数据包,这时候上面的mbox就会很快溢出,溢出的第一个包在pcb->refused_data里面会缓存一下,不过这个能力也很有限,等pcb->refused_data也存不下了这个pbuf就被直接丢弃了。这是在TCP_WND还没用完的情况下就丢包了。
更严重一点的情况,如果上面这种状况发生的同时lwip还在向上位机程序发数据的话,之前被丢弃的包很可能同时也是这些数据包的ACK,ACK丢了就会导致lwip向上位机重发这些数据,而上位机这边也在不停的重发(window没用完却丢包了必然会重发),然后两边就互不理睬各发各的。。。最后rst。断开。
这种情况有两个前提,一个是接受速度没那么快,这种情况我觉得还是比较容易发生的,比如一边收网络包一边发送到usart,波特率慢的时候网络接收就会很慢。
还有一个前提是上位机发来大量段数据包,且这些数据包总和x10 < TCP_WND。
目前还没想到很好的办法解决这个问题,加大mbox或者减小window只能稍微缓解一下,只要mbox的size<TCP_WND这个问题都会存在。个人觉得。。。这个问题有点严重,大家帮忙出出主意吧mbbill
帖子: 33
注册: 2009年 12月 20日 03:35 页首
--------------------------------------------------------------------------------
Re: 搞死LWIP的方法
由 bernard » 2010年 4月 18日 07:23
嗯,这个确实是个大问题。
修正下才是,mbox机制需要修改下,改成非mbox机制比较好。
我的邮件地址:
和RT-Thread相关的基础技术问题请在论坛上直接发帖,就不要发信给我啦,谢谢。bernard
帖子: 1362
注册: 2008年 5月 10日 07:22 页首
--------------------------------------------------------------------------------
Re: 搞死LWIP的方法
由 mbbill » 2010年 4月 18日 14:51
这样工作量有点大了
另外这个问题在优化lwip的时候也很可能发生,比如为了提高性能把TCP_BUF和TCP_WND定的比较大,这时候很可能就会发生这种现象,wireshark一抓全都是重发的包。mbbill
帖子: 33
注册: 2009年 12月 20日 03:35 页首
--------------------------------------------------------------------------------
Re: 搞死LWIP的方法
由 bernard » 2010年 4月 18日 16:14
工作量可能还行,可以考虑把mbox更改成数据队列的方式。只是这样的话,那么lwip那边tcp线程确实会修改一些了。这个加上,系能是否会好些呢。
找个时间验证一把。
一周热门 更多>