第五講 TCP程式設計

weixin_30924079發表於2020-04-04

******************
* 第五講 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/archive/2013/01/10/2854948.html

相關文章