2017-02-18 64 views
0

我正在實施C++客戶端服務器聊天程序以瞭解更多/練習套接字編程。我正在使用winsockV2。recv()字符數組大小

簡而言之, 客戶端程序連接到服務器,該服務器將客戶端套接字存儲在向量中 客戶端程序發送消息供服務器分發給向量中的其他客戶端。

我想我遇到的問題是,客戶端和服務器正在接收消息並將其存儲在char message[256]中,如果消息比256短,當我正在存儲的std::cout << message;中顯示奇怪的字符被告知是未初始化的內存。下面是一個輸出示例:

k:message from client to other client╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠(■o 

是否有某種方法可以創建接收消息大小的字符數組?即

char recvMessage[4096]; 
int s = recv(socket, recvMessage, sizeof(recvMessage),0); 
char recvOutput[strlen(recvMessage)] = recvMessage; 
std::cout << recvOutput << std::endl; 

否則,什麼是你的recv解決方案「荷蘭國際集團,你不知道的長度的消息?

如果我是一個完全白癡,請友好,我來自PHP。類是如下:

SVR.CPP

參見receiveMessages()distributeMessages()和功能

#include "stdafx.h" 
#include "svr.h" 

svr::svr() 
{ 
    //WSA Business I don't understand 
    WORD wVersionRequested; 
    WSADATA wsaData; 
    int err; 

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ 
    wVersionRequested = MAKEWORD(2, 2); 

    err = WSAStartup(wVersionRequested, &wsaData); 
    if (err != 0) 
    { 
     /* Tell the user that we could not find a usable */ 
     /* Winsock DLL.         */ 
     printf("WSAStartup failed with error: %d\n", err); 
    } 
    //End of WSA Business 

    //get addressSize 
    addressSize = sizeof(address); 
    //set address data members 
    address.sin_family = AF_INET; 
    address.sin_port = htons(444); 
    address.sin_addr.s_addr = INADDR_ANY; 

    //init sListen 
    sListen = socket(AF_INET, SOCK_STREAM, 0); 
    bind(sListen, (sockaddr*)&address, addressSize); 
} 

svr::~svr() 
{ 
} 

void svr::start() 
{ 
    std::thread newConnThread(&svr::newConnection, this); 
    newConnThread.join(); 
} 

void svr::receiveMessages(int clientIndex) 
{ 
    std::cout << "\tsvr::recv thread started for client index:" << clientIndex << std::endl; 

    //create char arr 
    char recvMessage[256]; 

    //forever 
    while (true) 
    { 
     //receive message and input it to recvMessage char arr. 
     recv(clients[clientIndex], recvMessage, sizeof(recvMessage), 0); 

     //if message is not null, send out to other clients 
     if (recvMessage != NULL) 
     { 
      std::cout << "\t\tINFO:Received message of length: " << std::strlen(recvMessage) << " size: " << sizeof(recvMessage) << " : " << recvMessage << std::endl; 
      distributeMessages(recvMessage, clientIndex); 
     } 
    } 
} 

//distributes messages to all clients in vector. called by receiveMessages function, normally in rMessages thread. 
void svr::distributeMessages(std::string message, int clientIndex) 
{ 
    for (unsigned int i = 0; i < clients.size(); i++) 
    { 
     if (clientIndex != i) 
     { 
      send(clients[i], message.c_str(), message.length(), 0); 
     } 
     else 
     { 
      //would have sent to self, not useful. 
     } 
    } 
} 

//accepts new connections and adds sockets to vector. 
void svr::newConnection() 
{ 
    //mark for accept, unsure of somaxconn value; 
    listen(sListen, SOMAXCONN); 

    std::cout << "\tSERVER: awaiting new connections..." << std::endl; 
    while (true) 
    { 
     //accept connection and push on to vector. 
     clients.push_back(accept(sListen, (sockaddr*)&address, &addressSize)); 

     //responds to new clients. 
     const char *message = "Hi, you've successfully connected!"; 

     int clientIndex = clients.size() - 1; 
     int sent = send(clients[clientIndex], message, 33, 0); 

     //start new receiveMessage thread 
     std::thread newClient(&svr::receiveMessages, this, clientIndex); 

     //detach here, let newConn thread operate without depending on receiveMessages 
     newClient.detach(); 
    } 
    std::cout << "\tSERVER: no longer listening for new connections" << std::endl; 
} 

CLI.CPP

參見cSend()cRecv()功能

#include "stdafx.h" 
#include "cli.h" 

cli::cli(char *ip) 
{ 
    //WSA 
    { 
     WORD wVersionRequested; 
     WSADATA wsaData; 
     int err; 

     // Use the MAKEWORD(lowbyte,highbyte) macro declared in windef.h 
     wVersionRequested = MAKEWORD(2, 2); 

     err = WSAStartup(wVersionRequested, &wsaData); 

     if (err != 0) 
     { 
      std::cout << "WSAStartup failed with the error: " << err; 
     } 
    } 

    //get addressSize 
    addressSize = sizeof(address); 

    //set address struct data members 
    address.sin_family = AF_INET; 
    address.sin_port = htons(444); 

    //if ip empty, prompt user; 
    if (ip == NULL) 
    { 
     std::string ipInput; 
     std::cout << "\n\tConnect to which IP: "; 
     std::cin >> ipInput; 
     address.sin_addr.s_addr = inet_addr(ipInput.c_str()); 
    } 
    else 
    { 
     address.sin_addr.s_addr = inet_addr(ip); 
    } 

    sock = socket(AF_INET, SOCK_STREAM, 0); 

    std::cout << "\n\tYour username: "; 
    std::cin >> uname; 
} 

cli::~cli() 
{ 
} 

void cli::start() 
{ 
    try 
    { 
     //hold string 
     char message[33]; 

     std::cout << "\n\tcli::start() called"; 
     int conRet; 

     //connects to server socket & receives a message, stores in it message variable 
     conRet = connect(sock, (sockaddr*)&address, (int)addressSize); 
     recv(sock, message, sizeof(message), 0); 

     std::cout << "\n\tSERVER: " << message; 

     //starts threads, pass this for object scope. 
     std::thread sendThread(&cli::cSend, this); 
     std::thread recvThread(&cli::cRecv, this); 

     //this function (start) will return/end when send and recv threads end. 
     sendThread.join(); 
     recvThread.join(); 
    } 
    catch (std::exception e) 
    { 
     std::cerr << e.what() << std::endl; 
    } 
} 

void cli::cSend() 
{ 
    std::cout << "\n\tcli::send thread started"; 
    //char arr for sending str; 
    std::string getLine; 
    while (true) 
    { 
     std::cout << "\n\t" << uname << ":" << std::flush; 
     //set to "" because i suspected the value remains in the string after a loop. 
     std::string message = ""; 

     //get input, put it in message 
     std::getline(std::cin, message); 

     //get full message 
     std::string fullMessage = uname + ":" + message; 

     //get constant int, size of fullMessage 
     const int charArrSize = fullMessage.length(); 

     std::cout << "\t\tINFO: Sending character array of length: " << charArrSize << " size: " << sizeof(fullMessage.c_str()) << " : " << fullMessage.c_str() << std::endl; 

     //sends it 
     send(sock, fullMessage.c_str(), charArrSize, 0); 
    } 
} 

void cli::cRecv() 
{ 
    std::cout << "\n\tcli::recv thread started"; 

    //initialize arr to 0, will hopefully help avoid the weird chars in the cout 
    char recvMessage[256]{ '\0' }; 

    while (true) 
    { 
     recv(sock, recvMessage, sizeof(recvMessage), 0); 

     std::cout << "\t\tINFO:Received message of length: " << std::strlen(recvMessage) << " size: " << sizeof(recvMessage) << " : " << recvMessage << std::endl; 

     std::cout << recvMessage << std::endl; 
    } 
} 

回答

3

您收到的郵件的解決方案是什麼?您不知道 的長度?

recv()告訴你它收到的消息的長度。你不必知道它是什麼。這是recv()的返回值。

int s = recv(socket, recvMessage, sizeof(recvMessage),0); 

看到 - 你去了。這就在你面前。這是s。當然,如果出現錯誤s將是負面的,你需要檢查。但是,忽略這些細節,您的擔憂已經結束:s是您剛收到的消息的長度。

char recvOutput[strlen(recvMessage)] = recvMessage; 

這不起作用。什麼是strlen()在這裏做? strlen()計算字符串的大小,期望該字符串是一個老式的C風格字符串,該字符串由\0字節終止。 recv()不會終止它以\0字節收到的任何內容。相反,它會返回實際的字符數。

而且,此外,這將無法正常工作。你不能以這種方式初始化數組。

顯而易見,這裏的意圖顯然是希望接收一個文本字符串作爲消息。好吧,既然你選擇的語言爲C++,你標記你的問題是這樣的,合乎邏輯的結論是,你應該使用什麼C++給你處理文本字符串:在std::string類:

std::string recvOutput{recvMessage, recvMessage+s}; 

有你走。任務完成。由於您已經知道s中收到的消息的長度,正如我們之前確定的(並且在仔細檢查s不是負數之後),您可以簡單地使用std::string的現有構造函數來初始化給定迭代器的新字符串,或一個指針,指向字符串的開始和結束。

在處理底層操作系統接口(如套接字)時,除了使用原始數據類型(如普通數組和緩衝區)之外別無選擇,因爲這是操作系統可以理解的唯一事情。但是,有了C++庫提供的豐富的模板和類,您的代碼應該在第一時間切換到使用C++類和模板,以便能夠使用所有這些資源。因此,只要您確定文本字符串recv()剛剛出現的大小,只需將其填入std::string,然後再確定如何處理它。

+1

感謝您的詳細信息。我沒想過要使用返回值! 使用std :: string構造函數,我知道第二個參數是大小,但爲什麼使用'recvMessage + s'? – spkvn