本帖最后由 冰炭寒酒 于 2013-7-9 22:54 编辑
使用arduino/mircro-arduino的ENC28J60及RJ45网口模块时, 一般采用 EtherCard 库 ,在IDE环境中增加库后,无需其他配置,就可以试着运行EtherCard中的sample示例,比如 getDHCPandDNS范例。但在一些环境下,程序运行时有下面两个问题:
问题1.执行到ether.dnsLookup(website)即解析给定的域名时,有些环境DNS解析这句大约超过30秒,甚至失败而执行 Serial.println("DNS failed"); 或此后的ether.printIp("Server: ", ether.hisip) 打印解析的ip为0.0.0.0
有些同学尝试把ether.dnsLookup(website) 改在loop代码中不断去取,其实这是有副作用的,并不可取,因为库文件注释里写道:
// use during setup, as this discards all incoming requests until it returns
bool EtherCard::dnsLookup (prog_char* name, bool fromRam)
也就是说,dnsLookup调用会压制其他的收发包处理。所以最好在setup中执行。
那么经过跟踪,最终查到问题在于:
库文件里,HOME_PATH\Arduino\libraries\ethercard\dns.cpp中,有一段如下代码:
bool EtherCard::dnsLookup (prog_char* name, bool fromRam) {
word start = millis();
while (!isLinkUp() || clientWaitingGw()) {
packetLoop(packetReceive());
if ((word) (millis() - start) >= 30000){
return false;
}
}
...
}
跟踪while中isLinkUp和clientWaitingGw这两个方法,分别是:1.不断测试物理端口状态,2.DHCP收到的网关IP地址通过ARP获取mac地址。如果这两步没有完成,将会一直循环,处理packet,直到30秒超时。 就是这里往往产生超时,返回错误。
奇怪的是,这两个方法有相关性,在一起执行时往往时间很长,分开执行,时间很快。
所以将上面代码修改一下,变为:
...
while(!isLinkUp()){
packetLoop(packetReceive());
if ((word) (millis() - start) >= 30000){
return false;
}
}
while(!clientWaitingGw()){
packetLoop(packetReceive());
if ((word) (millis() - start) >= 30000){
return false;
}
}
...
如果不想改库文件,其实,在setup中判断一下这两个方法的状态,当都这两个方法都执行结束之后再调用dnsLoopUp就可以了。
问题2. 还是这个示例程序getDHCPandDNS, loop方法中的 ether.browseUrl(),每次在callback中返回的长度总是小于512字节(包含http head总共小于512B),即使访问的页面大于512B.
原因在于:
还是库文件 HOME_PATH\Arduino\libraries\ethercard\tcpip.cpp 中的packetLoop()方法中,对默认状态下的连接会调用
make_tcp_ack_from_any(len,TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V); 熟悉TCP帧格式的同学应该知道,TCP_FLAGS_FIN_V 表示断开一个TCP连接。也就是说,默认情况下,如果一个页面会分在多个tcp包中,在第一个应答后连接就终止了,没有继续获取后面的包。
修改方式:网上有同学说去掉这个TCP_FLAGS_FIN_V,但应该这样会影响一些其他的逻辑。好在有一个方法:
ether.persistTcpConnection(true) ,有兴趣的同学可以到库文件里搜一下,这个方法做的正是设置状态,使调用变为: make_tcp_ack_from_any(len,TCP_FLAGS_PUSH_V); 即不去设置FLAGS_FIN_V位标志。 所以,库应该是做了考虑的,但是从DEMO程序中看不出这种使用方式,查了不少外文论坛找到这个答案。
综上两点,这个程序的代码可以改为如下,注意红色就是这两处更改:
// This demo does web requests via DHCP and DNS lookup.
// 2011-07-05 <[email protected]> http://opensource.org/licenses/mit-license.php
#include <EtherCard.h>
#define REQUEST_RATE 5000 // milliseconds
#define XB_DEBUG
// ethernet interface mac address
static byte mymac[] = { 0x74,0x69,0x69,0x2a,0x18,0x39 };
// remote website name
char website[] PROGMEM = "www.geek-workshop.com";
byte Ethernet::buffer[2048];
static long timer;
// called when the client request is complete
static void my_result_cb (byte status, word off, word len) {
Serial.print("<<< reply ");
Serial.print(len);
Serial.println(" data:");
Serial.print(millis() - timer);
Serial.println(" ms");
int i = 0;
while(i<len){
byte c = (const byte) (Ethernet::buffer[off + i]);
Serial.print((char)(c));
i++;
}
//Serial.println((const char*) Ethernet::buffer + off);
}
void initDns(){
long timer;
//waitting status
timer = millis();
while (!ether.isLinkUp() ) {
if( millis() - timer < 10000 )
ether.packetLoop(ether.packetReceive());
else break;
}
timer = millis();
while( ether.clientWaitingGw() ){
if( millis() - timer < 10000 )
ether.packetLoop(ether.packetReceive());
else break;
}
}
void setup () {
Serial.begin(57600);
Serial.println("\n[getDHCPandDNS]");
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
Serial.println( "Failed to access Ethernet controller");
if (!ether.dhcpSetup())
Serial.println("DHCP failed");
ether.printIp("My IP: ", ether.myip);
// ether.printIp("Netmask: ", ether.mymask);
ether.printIp("GW IP: ", ether.gwip);
ether.printIp("DNS IP: ", ether.dnsip);
#ifdef XB_DEBUG
ether.persistTcpConnection(true); // if notset, only 512bytes return in callback
initDns(); //speed waitting port and gateway arp
#endif
int i = 0;
while (!ether.dnsLookup(website) && (i++ < 5 ))
Serial.println("DNS failed");
ether.printIp("Server: ", ether.hisip);
timer = - REQUEST_RATE; // start timing out right away
}
void loop () {
ether.packetLoop(ether.packetReceive());
if (millis() > timer + REQUEST_RATE) {
timer = millis();
Serial.println("\n>>> REQ");
ether.browseUrl(PSTR("/"), "thread-2128-1-1.html", website, my_result_cb);
}
}
|