发信人: gdtyy (gdtyy), 信区: Embedded
标 题: 第五讲 TCP编程
发信站: 水木社区 (Mon Jun 25 23:31:47 2007), 站内
******************
* 第五讲 TCP编程 *
******************
2007/01/01 asdjf@163.com www.armecos.com
下面是TCP测试源码,略微不同的是一开始要先执行“init_all_network_interfaces()
;”,同时判断网卡是否安装(条件判断CYGHWR_NET_DRIVER_ETH0)。剩下的部分就全是标准
写法。这里用到的一些socket函数有阻塞功能,不需要额外延时,所以处理速度很快。此程
序配合IE浏览器使用,在浏览器里输入192.168.0.6<回车>就可以浏览到静态网页。
TCP测试源码
//此程序配合IE浏览器
#include <network.h>
#include <pkgconf/system.h>
#include <pkgconf/net.h>
#include <cyg/infra/testcase.h>
#ifdef CYGBLD_DEVS_ETH_DEVICE_H // Get the device config if it exists
#include CYGBLD_DEVS_ETH_DEVICE_H // May provide
CYGTST_DEVS_ETH_TEST_NET_REALTIME
#endif
#ifdef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS // do we use the rt test?
# ifdef CYGTST_DEVS_ETH_TEST_NET_REALTIME // Get the test ancilla if it exists
# include CYGTST_DEVS_ETH_TEST_NET_REALTIME
# endif
#endif
#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
static char stack[STACK_SIZE],stack1[STACK_SIZE];
static cyg_thread thread_data,thread_data1;
static cyg_handle_t thread_handle,thread_handle1;
unsigned char httpweb[]={
"HTTP/1.0 200 OK/r/n"
"Date: Mon, 24 Nov 2003 01:24:17 GMT/r/n"
"Server: microHttp/1.0 Zlgmcu Corporation/r/n"
"Accept-Ranges: bytes/r/n"
//"Content-Length: 116/r/n"//"Connection: Keep-Alive/r/n"
"Connection: Keep-Close/r/n"
"Content-Type: text/html/r/n"
"/r/n"
};
unsigned char web[]={
"<HTML>/r/n"
"<HEAD>/r/n"
"<TITLE>ARM_NET演示网页(周立功单片机)</TITLE>/r/n"
"<BODY aLink=green background=/100.bmp bgColor=#f1f1dd link=red/r/n"
"vLink=#321afd>/r/n"
"<H1>HELLO WELCOME TO EasyArm WEBSERVER</H1>/r/n"
"<UL>/r/n"
"<LI> <A HREF=/"http://www.zlgmcu.com//">周立功单片机网站 </A>/r/n"
"<LI> <A HREF=/"http://www.zlg.cn//">周立功单片机内部BBS </A>/r/n"
"<LI> <A HREF=/"http://www.zlgmcu.cn//">周立功单片机 </A>/r/n"
"</UL>/r/n"
"</BODY>/r/n"
"</HTML>/r/n"
};
unsigned char httpgif[]={
"HTTP/1.0 200 OK/r/n"
"Date: Mon, 24 Nov 2003 01:24:17 GMT/r/n"
"Server: microHttp/1.0 Zlgmcu Corporation/r/n"
"Accept-Ranges: bytes/r/n"
//"Content-Length: 116/r/n"//"Connection: Keep-Alive/r/n"
"Connection: Keep-Close/r/n"
"Content-Type: image/bmp/r/n"
"/r/n"
};
unsigned char bmp[442]={
0x42,0x4d,0xb6,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x28,0x00,
0x00,0x00,0x1a,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x04,0x00,0x00,0x00,
0x00,0x00,0x40,0x01,0x00,0x00,0xc4,0x0e,0x00,0x00,0xc4,0x0e,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,
0x00,0x00,0x00,0x80,0x80,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x80,
0x00,0x00,0xc0,0xc0,0xc0,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0xff,0x00,0x00,0xff,
0x00,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,
0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf6,0x66,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x66,0x66,0x6f,0x6f,0xff,0x66,0x66,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xf6,0xff,0xff,0x6f,0xf6,0xff,0xf6,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x6f,0xff,0x6f,0xf6,0xff,0xf6,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xf6,0xff,0xff,
0x6f,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x6f,0xf6,0xff,0x6f,0xff,
0x6f,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xf6,0x66,0x66,0xf6,0xff,0xf6,0x66,
0x6f,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00
};
void
pexit(char *s)
{
CYG_TEST_FAIL_FINISH(s);
}
void
webserver_test(struct bootp *bp)
{
//struct protoent *p;
//struct timeval tv;
struct sockaddr_in host,client;
int s,sa,e_source,len;
unsigned char buf[400];
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
pexit("socket");
return;
}
// Set up host address
host.sin_family = AF_INET;
host.sin_len = sizeof(host);
host.sin_addr.s_addr = INADDR_ANY;
host.sin_port = ntohs(80);
if(bind(s, (struct sockaddr *) &host, sizeof(host)) < 0) {
pexit("bind /source/ error");
}
listen(s, SOMAXCONN);
while(true){
memset(buf, 0, sizeof(buf));
if ((sa = accept(s, (struct sockaddr *)&client, &len)) < 0) {
printf("Accept ERROR!/n");
continue;
}
printf("SERVER : HTTP request arrived from %s:%d/n",
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
len = read(sa, buf, sizeof(buf));
if(buf[5] == ' '){
len = write(sa, httpweb, sizeof(httpweb)-1);
len = write(sa, web, sizeof(web));
}
else if(buf[5] == '1'){
len = write(sa, httpgif, sizeof(httpgif)-1);
len = write(sa, bmp,sizeof(bmp));
}
close(sa);
}
}
void
net_test(cyg_addrword_t p)
{
diag_printf("Start Networking Test.../n");
init_all_network_interfaces();
#ifdef CYGHWR_NET_DRIVER_ETH0
if (eth0_up) {
cyg_thread_create(10, // Priority - just a number
webserver_test, // entry
(cyg_addrword_t)ð0_bootp_data, // entry parameter
"Network tcp test", // Name
&stack1[0], // Stack
STACK_SIZE, // Size
&thread_handle1, // Handle
&thread_data1 // Thread data structure
);
cyg_thread_resume(thread_handle1); // Start it
}
#endif
}
void
cyg_start(void)
{
// Create a main thread, so we can run the scheduler and have time 'pass'
cyg_thread_create(10, // Priority - just a number
net_test, // entry
0, // entry parameter
"Network test", // Name
&stack[0], // Stack
STACK_SIZE, // Size
&thread_handle, // Handle
&thread_data // Thread data structure
);
cyg_thread_resume(thread_handle); // Start it
cyg_scheduler_start();
}
附录:关于在各种介质上提高TCP速率的分析 2006/03/13
TCP的传输速率取决于重传延时,只要尽量避免重传,就能提高传输速度。TCP通过CRC
检错和错误重传机制实现可靠传输。TCP传输速度 = 有效传输字节数 / 传输时间 。在传输
字节数相同的情况下,减少传输时间就可以提高速度。
传输时间由CPU处理时间、传输往返时间、重传消耗时间三部分组成。
其中,CPU处理时间一般可以忽略不计,因为现代的CPU单条指令执行时间是ns或us量级
的,而TCP定时器是200ms量级的,即使代码量膨胀上万倍,所消耗的时间与TCP定时器时间
比起来也微乎其微。
传输往返时间是指从发出数据到接收到应答所耗费的时间,这个时间不能避免,不过,
通过采用滑动窗技术,可以在一个往返时间内传输更多的数据,比停等协议显著提高了速度
。在信号质量好的信道内,不妨将接收对告窗口设置得大些(如65535),这样,在接收到应
答前,可以传输更多数据。另外,延迟200ms应答可以积攒更多数据,一次性发出,减少小
数据包造成的头部开销浪费,这个延迟是有益的。
重传是由于拥塞丢包、误码丢包、坏包、超时引起的,这是影响TCP传输速度最主要的
原因,应尽量避免。虽然TCP是面向连接的协议,但它不像电路交换那样拥有信令通道,可
以获得完备的链路信息。TCP不清楚当前链路情况,只能通过事后观察现象,试探或者推测
出相关信息,例如往返时间,拥塞情况,链路恢复,丢包等等。这种“马后炮”式的做法势
必造成不必要的时间浪费,此时事先预防比事后补救要有效得多。比如:事先开辟一个大的
缓冲区按顺序缓存收到的有效包,再组装成一大块连续的数据块发送到应用层,这样就避免
了人为地丢弃乱序包造成的丢包重传,而且即使在出现坏包的情况下也不用重传所有已经有
效到达的数据包。不过,因为增加了排序组装、包缓冲区、内存管理代码,所以内存的占用
量肯定会增大。
标准的BSD TCP协议栈是很早以前在铜线网络上实现的,现在出现了很多新的传输介质
,他们的特性对TCP传输速度有显著影响:
(1)无线网络
无线网络因其可移动特性,应用越来越多,如GPRS、CDMA、载波等。它的特点是误
码率高,带宽小。当越区切换或者信号不好时可能会造成误码丢包,但是标准的BSD协议栈
假设所有的丢包都是由于拥塞引起的,这在低误码率的铜线网络上是成立的,但在无线网络
里,频繁的误码丢包会造成拥塞窗长时间保持在小窗口状态,即使无线信道恢复正常,拥塞
控制算法仍然限制发送速率,尽管此时并没有发生拥塞。因此,在无线介质上的TCP协议栈
要修正标准BSD协议栈的已经失效的假设,使其保持激进的发送策略,充分榨取宝贵的无线
带宽,同时保持小的额外开销。改进方法......
(2)卫星通道
卫星通道的特点是延迟大,误码率低,带宽大。TCP若想保持高速,必须增大发送
窗口,避免重传。比如:发送了10K数据,多次重传花费了5秒的时间,那么传输速率只有区
区2K。避免重传的一个有效方法是预防性重发,一个包发好几遍,减小出错概率,不过带宽
浪费大,反正卫星通道的带宽很大,用带宽换速度也划算。
(3)高速光纤网络
高速光纤网络的特点是误码率极低,带宽极大。按理说误码率低的话,标准TCP协
议应该能很好地工作,不过,即使是误码率极低的情况,也还是会有出现误码的时候,此时
,问题来了。丢包时,标准TCP会启动拥塞控制,它的恢复速度在高带宽低误码下显得过于
保守,虽然相对损耗比例比较小,但绝对带宽损失大(会损失几十兆带宽),对于想榨干带宽
利用率的我们来说,这是不能忍受的。此时需要增加更激进的快速恢复算法,用空间换时间
。
(4)铜线网络
标准BSD协议栈就是在铜线网络上开发的,铜线网络的特点是误码率低,带宽大。
TCP假设一切丢包都由拥塞引起。拥塞控制会影响效率,但能保证公平性,最终实现整体效
率最高。
TCP协议的速度还与任务划分有关,应该合理地安排TCP任务的优先级,使其能够获得充
足的时间片。
另外,ZLGIP的状态机写法是状态驱动的,我认为最好改成事件驱动,即在事件中判断
状态,而不是在状态里判断事件,这样虽然增加了代码量,但思路更清晰。如下:
发生关闭事件函数() //事件驱动
{
switch(state)
case SYN:
case SYN_SENT:
case ESTABLISHED:
case FIN_WAIT1:
case FIN_WAIT2:
....
}
收到RST、SYN、FIN、ACK、数据包事件函数() //事件驱动
{
switch(state)
case SYN:
case SYN_SENT:
case ESTABLISHED:
case FIN_WAIT1:
case FIN_WAIT2:
....
}
而不是按如下写法:
Tcp_Established() //状态驱动
{
if(TCP_RST+TCP_SYN)
else if(TCP_FIN)
else if(TCP_ACK)
......
}
详见《状态机的两种写法》。
综上,TCP的代码越庞大,速度越快,短小“精悍”的代码注定快不了,你要根据具体
应用决定选择短小或者选择快速。一般TCP的数据最终都要交给硬件收发(串口/网卡),这些
硬件能够自动以相应的速度收发数据,而且现在的CPU处理速度也不是瓶颈,所以,影响
TCP速度的关键因素是算法。代码越大,意味着预防措施、异常处理、快速恢复等做得越充
分,从而,减少重传,加快TCP速度。
--
※ 来源:·水木社区 http://newsmth.net·[FROM: 61.149.56.*]