我正在实现一个C++客户端服务器聊天程序,以学习更多/实践套接字编程。我在用winsockV2。
简单地说,客户端程序连接到服务器,服务器将客户端套接字存储在向量客户端程序中,将消息发送给服务器以分发给向量中的其他客户端。
我认为我遇到的问题是,客户端和服务器正在接收消息并将其存储在char message[256]中,如果消息小于256个,当我被告知为未初始化的内存时,会显示奇怪的字符。下面是输出的一个示例:
k:message from client to other client╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠(■o是否有办法创建与接收到的消息大小相同的字符数组?i.e
char recvMessage[4096];
int s = recv(socket, recvMessage, sizeof(recvMessage),0);
char recvOutput[strlen(recvMessage)] = recvMessage;
std::cout << recvOutput << std::endl;否则,您对recv'ing消息的解决方案是什么,您不知道它的长度?
如果我是个十足的白痴,请仁慈点,我来自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;
}
}发布于 2017-02-18 02:48:30
对于您不知道的消息长度,您的解决方案是什么?
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的现有构造函数来初始化新的字符串,给出一个迭代器或指针,它指向字符串的开始和结束。
在处理低级操作系统接口(如套接字)时,您别无选择,只能使用原始数据类型,比如普通的char数组和缓冲区,因为这是操作系统唯一理解的东西。但是,有了C++库提供的丰富的模板和类集,您的代码应该立即切换到使用C++类和模板,以便能够使用所有这些资源。因此,一旦您确定了文本字符串recv()的大小,就先将其填充到一个std::string中,然后再找出该如何处理它。
https://stackoverflow.com/questions/42310469
复制相似问题