c++ 通信演進level1 ----單線程同步阻塞通信

  本篇,紀錄一個在 windows上使用 socket進行通信的例子,代碼源自於網上。由於時間過去挺久了,當時我也沒有加書籤,現在暫時還不好找出處。 文中給出一些關鍵代碼片段,一方面用於鞏固我所學的知識,另一方面,用於縱向的技術對比,加深理解。完整的地址在這裏:地址。 

  首先看看項目結構:

 服務端的關鍵代碼:

int main(){
    //加載Winsock庫,初始化socket資源
    initialization();
    //創建套接字
    SOCKET s_server;
    s_server = socket(AF_INET,SOCK_STREAM,0);
    //綁定信息
    bindInfo(&s_server);
    //將socket設置爲lisen狀態
    changtoListen(&s_server);
    SOCKET commandSock;
    responseAccept(commandSock,&s_server);
    //通信
    onInteract(&commandSock);
    //結束
    onClose(&commandSock,&s_server);
    return 0;
}

  總體而言,就是經過標準的socket步驟: 生成本地socket->bind()  -> listen() -> accept() -> 通信傳輸 -> 關閉;

  這個過程中,關鍵點在於accept(),在本次代碼中,它是同步阻塞的,代碼如下:

//響應連接狀態
int responseAccept(SOCKET& _sock,SOCKET* _s_server){
    int len = sizeof(SOCKADDR);
    SOCKADDR_IN accept_addr;
    SOCKET s_accept= accept(*_s_server,  (SOCKADDR *)&accept_addr, &len);
    if (s_accept==SOCKET_ERROR){
        std::cout << "連接失敗!錯誤碼:"<<WSAGetLastError() <<std::endl;
        WSACleanup();
        return 0;
    }
    _sock = s_accept;
    std::cout << "連接建立,準備接受數據!" <<std::endl;
}

   接收的結果是 套接字對象;同時,這個接收的過程是 阻塞的。   與客戶端的所有通信將基於當前接收的套接字對象進行。 

   我將 服務器與客戶端的通信 抽象爲一個方法,稱之爲  互動 onInteract(),代碼如下:

//利用返回的套接字進行通信
void onInteract(SOCKET* s_accept){
    int recv_len =-1;
    int send_len = -1;
    char recv_buf[100];
    char send_buf[100];
    while (1){
        recv_len = recv(*s_accept,recv_buf,100,0);
        if (recv_len<0){
            std::cout <<"接受失敗!錯誤碼:"<<WSAGetLastError()<<std::endl;
            break;
        }else if(recv_len==0){
            std::cout <<"會話結束!"<<std::endl;
        } else{
            std::cout <<"客戶端信息:"<<recv_buf<<std::endl;
        }
        std::cout <<"請輸入回覆信息:";
        std::cin>> send_buf;
        send_len = send(*s_accept,send_buf,100,0);
        if(send_len<0){
            std::cout <<"發送失敗!錯誤碼:"<<WSAGetLastError() <<std::endl;
            break;
        }
    }
}

  本地代碼的關鍵點在於: recv(),與  send()  都是阻塞的方式。 它的表現是: 一次發送,一次接收 如此循環往復。  此外,還應對接收到的信息進行判別,用於控制通信的結束,也就形成了實際上的 應用層協議(但是在本例子中還未加,由於此時不想修改代碼,確實沒有精力)。 

    接着,再看一看客戶端的代碼:

int main(){
    initialization();
    SOCKET s_server;
    s_server = socket(AF_INET,SOCK_STREAM,0);
     onConnect(s_server);
    onActive(s_server);
    onClose(s_server);
    return 0;
}

    它的標準流程則是:  生成本地socket -> connect() -> 消息通信的過程 -> 關閉。 

    這其中的注意的是: connect()該方法是立即返回的,連接由tcp/udp進行控制。  之後通信的過程,我依然是抽象成的一個方法叫做: onActive(),代碼如下:

//與服務器進行通信
void onActive(SOCKET& s_server){
    char send_buf[100];
    char recv_buf[100];
    int recv_len;
    int send_len;
    while (1){
        std::cout<<"請輸入發送信息:";
        std::cin>>send_buf;
        send_len = send(s_server,send_buf,100,0);
        if (send_len<0){
            std::cout <<"發送失敗!"<<std::endl;
            break;
        }
        recv_len = recv(s_server,recv_buf,100,0);
        if (recv_len<0){
            std::cout <<"接收失敗!"<<std::endl;
            break;
        } else{
            std::cout <<"服務端信息:"<<recv_buf<<std::endl;
        }
    }
}

  其中的關鍵仍然是send()方法,recv()方法,它們當然也是阻塞的

  最終的效果如下: 

總結: 

    1.在實踐過程中,空格字符會被 處理成 分段操作,譬如 "ping hello",  會理解爲兩次 send。這說明了在socket的讀寫字節流中,控制字符有可能大部分要經過轉義

    2.當前方式實現中,讀寫操作是交替運行的。並且,讀寫操作是阻塞的(發送和接收是一種另類的讀寫操作)

    3.當前實現中,服務器是單線程的,不要搞混淆了,接收到的是 socket,而不是thread。  這也意味着,一次只能接入一個會話(或者接收到的多個會話進行線性處理。)。(因爲通話的過程是基於接收到的socket句柄而進行的.) 。 此外,socket與線程並沒有必然聯繫,它們實際上代表了兩種資源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章