#include <stdio.h>
#include <mem.h>
#include <wattcp.h>
int main(int argc, char *argv[])
{
unsigned long host = -1;//默认是广播地址,即可以接收一切主机发送来的消息
udp_Socket s;
char myaddr[20], hisaddr[20];
char strData[512];// 接收到的数据
char strBuffer[2048];// 缓冲
int blGotdata = 0, iPort = 5150, iMyPort = 5150;
sock_init();
if ( !udp_open(&s, iMyPort, host, iPort, NULL))
{
puts("Could not open broadcast socket");
return 3;
}
if ( sock_recv_init(&s, strBuffer, sizeof(strBuffer)))
{
puts("Could not enable large buffers");
return 3;
}
while(1)
{
tcp_tick(&s);
memset(strData, 0, strlen(strData));
blGotdata = sock_recv_from(&s, (long *)&host, (word *)&iPort, strData, sizeof(strData), NULL);
if(blGotdata > 0)
{
printf("[%s:%ld] send me: %s\n", inet_ntoa((char *)hisaddr, (unsigned long)intel(host)), (unsigned long)intel16(iPort), strData);
}
}
return 0;
}
上面是一个完完整整的接受UDP协议数据的客户端程序.
首先,需要对Wattcp进行初始化。这个过程是通过调用sock_init();其调用关系是:
sock_init() ==> sock_init_noexit() ==> tcp_init_noexit() ==> _eth_init() ==> _pkt_eth_init() ==> pkt_init()
对于pkt_init()函数我在最初《初来乍到》中就有过介绍,它的主要作用就是初始化网卡,调整网卡的工作模式,并告诉网卡驱动我也需要接受/发送网络数据包。
从这个初始化过程我们就可以看到处理TCP/IP协议所使用的层数了,三层。最上面是socket,主要是封装,便于使用。中间是协议,包括TCP、IP等协议的处理。最下边是硬件,也就是驱动程序、硬件设备等等。其中sock_init() ==> sock_init_noexit()
属于最上层,tcp_init_noexit()
是中间层,`_eth_init() ==> _pkt_eth_init() ==> pkt_init()
是最下层。
在linux下,最下层可能不是网络,而是进程间通信。于是,成为一种进程间通信的方式。读linux内核源码时,着实被其处理方式折服了一番。
接下来就是告诉Wattcp我们使用什么协议进行通信,以及用于通信的一些基本信息。udp_open()
就是用来完成这个工作的。这个函数实际上仅仅是对传入的套接字进行了初始化。当然,也使用Arp协议来获取了服务器端的MAC地址。但是,Arp协议比较简单,就留给感兴趣的自己分析吧。只要顺着_arp_resolve()
看下去就可以了。
sock_recv_init()
也是进行初始化工作,它是给Wattcp提供一个数据缓冲区,让Wattcp把接受到数据先存放到这儿来。
最后就是数据的取出了,我们通过调用sock_recv_from()
来完成。实际上,sock_recv_from()
仅仅是从前面调用sock_recv_init()
设置的缓冲区种拷贝出来。如果长时间不往外拷贝并且缓冲使用完了,后面的包就会覆盖掉前面的包。于是,出现丢包现象。之所以DOS下容易出现丢包现象就是因为缓冲区太小,数据来不及处理就丢掉了。
您看,实际上数据的接受是非常简单的。同样,数据的发送也是一样的简单。记住“源码之下了无秘密”。