第五讲 TCP编程zz

来源:互联网 发布:h5页面 知乎 编辑:程序博客网 时间:2024/06/01 07:29

发信人: 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)&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://newsmth.net·[FROM: 61.149.56.*]
 

原创粉丝点击