第五讲 TCP编程

******************
* 第五讲 TCP编程 *
******************
    下面是TCP测试源码,略微不同的是一开始要先执行“init_all_network_interfaces();”,同时判断网卡是否安装(条件判断CYGHWR_NET_DRIVER_ETH0)。剩下的部分就全是标准写法。这里用到的一些socket函数有阻塞功能,不需要额外延时,所以处理速度很快。此程序配合IE浏览器使用,在浏览器里输入192.168.0.6<回车>就可以浏览到静态网页。
TCP测试源码
//此程序配合IE浏览器
#i nclude <network.h>
#i nclude <pkgconf/system.h>
#i nclude <pkgconf/net.h>
#i nclude <cyg/infra/testcase.h>
#ifdef CYGBLD_DEVS_ETH_DEVICE_H    // Get the device config if it exists
#i nclude 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)&eth0_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://blog.21ic.com/user1/5586/archives/2009/57918.html

原文地址:https://www.cnblogs.com/kuainiao/p/2854948.html