spk*_*kvn 0 c++ sockets winsock
我正在致力于实现一个 C++ 客户端服务器聊天程序以了解更多/练习套接字编程。我正在使用winsockV2。
\n\n简而言之,\n客户端程序连接到服务器,服务器将客户端套接字存储在向量中\n客户端程序发送消息让服务器分发到向量中的其他客户端。
\n\n我认为我遇到的问题是客户端和服务器正在接收消息并将其存储在 a 中,char message[256]如果消息短于 256,当我std::cout << message;被告知未初始化内存时,会显示奇怪的字符。这是输出的示例:
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\nRun Code Online (Sandbox Code Playgroud)\n\n有没有某种方法可以创建接收消息大小的字符数组?IE
\n\nchar recvMessage[4096];\nint s = recv(socket, recvMessage, sizeof(recvMessage),0);\nchar recvOutput[strlen(recvMessage)] = recvMessage;\nstd::cout << recvOutput << std::endl;\nRun Code Online (Sandbox Code Playgroud)\n\nrecv否则,对于您不知道长度的消息,您的解决方案是什么?
如果我是个彻头彻尾的白痴,请客气一点,我来自 PHP。课程如下:
\n\n查看receiveMessages()和distributeMessages()功能
#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 }\nRun Code Online (Sandbox Code Playgroud)\n\n查看cSend()和cRecv()功能
#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}\nRun Code Online (Sandbox Code Playgroud)\n
对于接收不知道长度的消息,您的解决方案是什么?
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 风格字符串\0。recv()不会终止它接收到的任何字节\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然后再弄清楚如何处理它。