首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >recv()字符数组大小

recv()字符数组大小
EN

Stack Overflow用户
提问于 2017-02-18 02:39:21
回答 1查看 1.3K关注 0票数 0

我正在实现一个C++客户端服务器聊天程序,以学习更多/实践套接字编程。我在用winsockV2。

简单地说,客户端程序连接到服务器,服务器将客户端套接字存储在向量客户端程序中,将消息发送给服务器以分发给向量中的其他客户端。

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

代码语言:javascript
复制
k:message from client to other client╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠(■o

是否有办法创建与接收到的消息大小相同的字符数组?i.e

代码语言:javascript
复制
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()函数

代码语言:javascript
复制
#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()函数

代码语言:javascript
复制
#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;
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-02-18 02:48:30

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

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

代码语言:javascript
复制
 int s = recv(socket, recvMessage, sizeof(recvMessage),0);

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

代码语言:javascript
复制
 char recvOutput[strlen(recvMessage)] = recvMessage;

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

而且,无论如何,这都行不通。不能以这种方式初始化数组。

显然,您在这里的明显目的是希望接收一个文本字符串作为消息。既然您选择的语言是C++,并且您将问题标记为这样,那么逻辑上的结论是,您应该使用C++提供的处理文本字符串的方法:std::string类:

代码语言:javascript
复制
std::string recvOutput{recvMessage, recvMessage+s};

这就对了。任务完成。由于您已经知道了s中接收到的消息的长度,正如我们之前所确定的(并且在反复检查s不是负值之后),您可以简单地使用std::string的现有构造函数来初始化新的字符串,给出一个迭代器或指针,它指向字符串的开始和结束。

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

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42310469

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档