recv() 字符数组大小

spk*_*kvn 0 c++ sockets winsock

我正在致力于实现一个 C++ 客户端服务器聊天程序以了解更多/练习套接字编程。我正在使用winsockV2。

\n\n

简而言之,\n客户端程序连接到服务器,服务器将客户端套接字存储在向量中\n客户端程序发送消息让服务器分发到向量中的其他客户端。

\n\n

我认为我遇到的问题是客户端和服务器正在接收消息并将其存储在 a 中,char message[256]如果消息短于 256,当我std::cout << message;被告知未初始化内存时,会显示奇怪的字符。这是输出的示例:

\n\n
k:message from client to other client\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0\xe2\x95\xa0(\xe2\x96\xa0o\n
Run Code Online (Sandbox Code Playgroud)\n\n

有没有某种方法可以创建接收消息大小的字符数组?IE

\n\n
char recvMessage[4096];\nint s = recv(socket, recvMessage, sizeof(recvMessage),0);\nchar recvOutput[strlen(recvMessage)] = recvMessage;\nstd::cout << recvOutput << std::endl;\n
Run Code Online (Sandbox Code Playgroud)\n\n

recv否则,对于您不知道长度的消息,您的解决方案是什么?

\n\n

如果我是个彻头彻尾的白痴,请客气一点,我来自 PHP。课程如下:

\n\n

SVR.CPP

\n\n

查看receiveMessages()distributeMessages()功能

\n\n
#include "stdafx.h"\n#include "svr.h"\n\nsvr::svr()\n{\n    //WSA Business I don\'t understand\n    WORD wVersionRequested;\n    WSADATA wsaData;\n    int err;\n\n    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */\n    wVersionRequested = MAKEWORD(2, 2);\n\n    err = WSAStartup(wVersionRequested, &wsaData);\n    if (err != 0)\n    {\n        /* Tell the user that we could not find a usable */\n        /* Winsock DLL.                                  */\n        printf("WSAStartup failed with error: %d\\n", err);\n    }\n    //End of WSA Business\n\n    //get addressSize\n    addressSize = sizeof(address);\n    //set address data members\n    address.sin_family = AF_INET;\n    address.sin_port = htons(444);\n    address.sin_addr.s_addr = INADDR_ANY;\n\n    //init sListen\n    sListen = socket(AF_INET, SOCK_STREAM, 0);\n    bind(sListen, (sockaddr*)&address, addressSize);\n}\n\nsvr::~svr()\n{\n}\n\nvoid svr::start()\n{\n    std::thread newConnThread(&svr::newConnection, this);\n    newConnThread.join();\n}\n\nvoid svr::receiveMessages(int clientIndex)\n{\n    std::cout << "\\tsvr::recv thread started for client index:" << clientIndex << std::endl;\n\n    //create char arr\n    char recvMessage[256];\n\n    //forever\n    while (true)\n    {\n        //receive message and input it to recvMessage char arr.\n        recv(clients[clientIndex], recvMessage, sizeof(recvMessage), 0);\n\n        //if message is not null, send out to other clients\n        if (recvMessage != NULL)\n        {\n            std::cout << "\\t\\tINFO:Received message of length: " << std::strlen(recvMessage) << " size: " << sizeof(recvMessage) << " : " << recvMessage << std::endl;\n            distributeMessages(recvMessage, clientIndex);\n        }\n    }\n}\n\n//distributes messages to all clients in vector. called by receiveMessages function, normally in rMessages thread.\nvoid svr::distributeMessages(std::string message, int clientIndex)\n{\n    for (unsigned int i = 0; i < clients.size(); i++)\n    {\n        if (clientIndex != i)\n        {\n            send(clients[i], message.c_str(), message.length(), 0);\n        }\n        else\n        {\n            //would have sent to self, not useful.\n        }\n    }\n}\n\n//accepts new connections and adds sockets to vector.\nvoid svr::newConnection()\n{\n    //mark for accept, unsure of somaxconn value;\n    listen(sListen, SOMAXCONN);\n\n    std::cout << "\\tSERVER: awaiting new connections..." << std::endl;\n    while (true)\n    {\n        //accept connection and push on to vector.\n        clients.push_back(accept(sListen, (sockaddr*)&address, &addressSize));\n\n        //responds to new clients.\n        const char *message = "Hi, you\'ve successfully connected!";\n\n        int clientIndex = clients.size() - 1;\n        int sent = send(clients[clientIndex], message, 33, 0);\n\n        //start new receiveMessage thread\n        std::thread newClient(&svr::receiveMessages, this, clientIndex);\n\n        //detach here, let newConn thread operate without depending on receiveMessages\n        newClient.detach();\n    }\n    std::cout << "\\tSERVER: no longer listening for new connections" << std::endl;\n }\n
Run Code Online (Sandbox Code Playgroud)\n\n

命令行界面

\n\n

查看cSend()cRecv()功能

\n\n
#include "stdafx.h"\n#include "cli.h"\n\ncli::cli(char *ip)\n{\n    //WSA\n    {\n        WORD wVersionRequested;\n        WSADATA wsaData;\n        int err;\n\n        // Use the MAKEWORD(lowbyte,highbyte) macro declared in windef.h\n        wVersionRequested = MAKEWORD(2, 2);\n\n        err = WSAStartup(wVersionRequested, &wsaData);\n\n        if (err != 0)\n        {\n            std::cout << "WSAStartup failed with the error: " << err;\n        }\n    }\n\n    //get addressSize \n    addressSize = sizeof(address);\n\n    //set address struct data members\n    address.sin_family = AF_INET;\n    address.sin_port = htons(444);\n\n    //if ip empty, prompt user;\n    if (ip == NULL)\n    {\n        std::string ipInput;\n        std::cout << "\\n\\tConnect to which IP: ";\n        std::cin >> ipInput;\n        address.sin_addr.s_addr = inet_addr(ipInput.c_str());\n    }\n    else\n    {\n        address.sin_addr.s_addr = inet_addr(ip);\n    }\n\n    sock = socket(AF_INET, SOCK_STREAM, 0);\n\n    std::cout << "\\n\\tYour username: ";\n    std::cin >> uname;\n}\n\ncli::~cli()\n{\n}\n\nvoid cli::start()\n{\n    try\n    {\n        //hold string\n        char message[33];\n\n        std::cout << "\\n\\tcli::start() called";\n        int conRet;\n\n        //connects to server socket & receives a message, stores in it message variable\n        conRet = connect(sock, (sockaddr*)&address, (int)addressSize);\n        recv(sock, message, sizeof(message), 0);\n\n        std::cout << "\\n\\tSERVER: " << message;\n\n        //starts threads, pass this for object scope.\n        std::thread sendThread(&cli::cSend, this);\n        std::thread recvThread(&cli::cRecv, this);\n\n        //this function (start) will return/end when send and recv threads end.\n        sendThread.join();\n        recvThread.join();\n    }\n    catch (std::exception e)\n    {\n        std::cerr << e.what() << std::endl;\n    }\n}\n\nvoid cli::cSend()\n{\n    std::cout << "\\n\\tcli::send thread started";\n    //char arr for sending str;\n    std::string getLine;\n    while (true)\n    {\n        std::cout << "\\n\\t" << uname << ":" << std::flush;\n        //set to "" because i suspected the value remains in the string after a loop.\n        std::string message = "";\n\n        //get input, put it in message\n        std::getline(std::cin, message);\n\n        //get full message\n        std::string fullMessage = uname + ":" + message;\n\n        //get constant int, size of fullMessage\n        const int charArrSize = fullMessage.length();\n\n        std::cout << "\\t\\tINFO: Sending character array of length: " << charArrSize << " size: " << sizeof(fullMessage.c_str()) << " : " << fullMessage.c_str() << std::endl;\n\n        //sends it\n        send(sock, fullMessage.c_str(), charArrSize, 0);\n    }\n}\n\nvoid cli::cRecv()\n{\n    std::cout << "\\n\\tcli::recv  thread started";\n\n    //initialize arr to 0, will hopefully help avoid the weird chars in the cout\n    char recvMessage[256]{ \'\\0\' };\n\n    while (true)\n    {\n        recv(sock, recvMessage, sizeof(recvMessage), 0);\n\n        std::cout << "\\t\\tINFO:Received message of length: " << std::strlen(recvMessage) << " size: " << sizeof(recvMessage) << " : " << recvMessage << std::endl;\n\n        std::cout << recvMessage << std::endl;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Sam*_*hik 5

对于接收不知道长度的消息,您的解决方案是什么?

recv()告诉您它收到的消息的长度。您不必想知道它是什么。这就是recv()返回值。

 int s = recv(socket, recvMessage, sizeof(recvMessage),0);
Run Code Online (Sandbox Code Playgroud)

看——就这样。它就在你面前。它是s。当然,如果有错误s将会是负面的,你需要检查一下。但是,忽略这个小细节,您的担忧就结束了:s您刚刚收到的消息的长度。

 char recvOutput[strlen(recvMessage)] = recvMessage;
Run Code Online (Sandbox Code Playgroud)

那是行不通的。在这里做什么strlen()strlen()计算字符串的大小,期望该字符串是一个以字节结尾的老式 C 风格字符串\0recv()不会终止它接收到的任何字节\0。相反,它返回实际的字符数。

而且,除此之外,这无论如何也行不通。您不能以这种方式初始化数组。

显然,您在这里的明显意图是期望收到文本字符串作为消息。好吧,既然您选择的语言是C++,并且您这样标记了您的问题,那么合乎逻辑的结论是您应该使用C++让您处理文本字符串的东西:类std::string

std::string recvOutput{recvMessage, recvMessage+s};
Run Code Online (Sandbox Code Playgroud)

就这样吧。任务完成。由于您已经知道 中收到的消息的长度s,正如我们之前确定的那样(并且在仔细检查后发现结果s不是负值),因此您可以简单地使用std::string的现有构造函数来初始化给定迭代器或指针的新字符串,到字符串的开头和结尾。

在处理低级操作系统接口(如套接字)时,您别无选择,只能使用原始数据类型,如普通char数组和缓冲区,因为这是操作系统唯一能理解的东西。但是,由于 C++ 库提供了丰富的模板和类,您的代码应该立即切换到使用 C++ 类和模板,以便能够使用所有这些资源。因此,一旦确定了recv()刚刚产生的文本字符串有多大,只需将其填充到 a 中,std::string然后再弄清楚如何处理它。