2017-02-18 2 views
0

Я работаю над реализацией программы чата на клиентском сервере C++, чтобы узнать больше/практиковать программирование сокетов. Я использую winsockV2.recv() char array size

Коротко, Клиентская программа подключается к серверу, который хранит клиентский сокет в векторной программе , и отправляет сообщения для сервера для распространения другим клиентам в векторе.

Проблема, о которой я думаю, столкнулась с тем, что клиенты и сервер получают сообщение и сохраняют его в 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

Смежные вопросы