c++ 通过TCP套接字发送和接收字符串

cgyqldqp  于 2023-03-20  发布在  其他
关注(0)|答案(1)|浏览(209)

所以,我尝试用C++中的套接字编程在客户端和服务器之间发送数据。现在,我在StackOverflow中读到了一个答案,其中提到我们必须检查每次发送时接收到多少原始msg,然后等待,直到我们完全获得信息。为此,我使用了以下sendMsg()和receive()函数。

string receive()
    {
        string msg = "";
        char last_character = '%';
        do
        {
            char buffer[2048] = {0};
            read(sockfd, buffer, 2048);
            string s(buffer);
            msg += s;
            last_character = s[s.size() - 1];
        } while (last_character != '|');
        return msg.substr(0, msg.size() - 1);
    }
    bool sendMsg(string s)
    {
        s += '|';
        char buffer[s.size()];
        strcpy(buffer, &s[0]);
        int bytes_total = 0;
        int bytes_left = s.length();
        int bytes_now = 0;
        while (bytes_left > 0)
        {
            bytes_now = send(sockfd, &buffer[bytes_total], bytes_left, 0);
            if (bytes_now == -1)
            {
                err("Error sending message");
                return false;
            }
            assert(bytes_now <= bytes_left);
            bytes_left -= bytes_now;
            bytes_total += bytes_now;
        }

        return true;
    }

这在大多数情况下都很好用,只是有时会添加一个“|'字符,即消息前的分隔符。当我将一个新客户端连接到服务器,并尝试向客户端发送一些消息以在其控制台中打印时,这个问题就很突出。类似于以下内容:

usernames[username] = client_socket;
    comm.sendMsg(string(GRN) + "Username accepted.\n" + string(NRM));
    cout << GRN << username << " connected." << NRM << endl;
    comm.sendMsg(readme(username));

其中,用户名是一个无序Map,它存储每个唯一用户名的客户端套接字,自述文件函数只是一个字符串,如“欢迎来到聊天室“,+用户名。这里,#define NRM“\x1B[0m”,#define GRN“\x1B[32m”
所以,有时我得到的输出是

Enter the IP address of the server: l
Connected to server.
Enter your username: user
Username accepted.
|
+------------------------------------------------------------+
|                                                            |
|   [server]: Welcome to the chatroom, user                  |
|                                                            |
|   [server]: Here are the commands you can use:             |
|                                                            |
|   1. status             : Lists the status of all users    |
|   2. connect [username] : Connect to username to start     |
|                           chatting                         |
|   3. goodbye            : Ends current chatting session    |
|   4. close              : Disconnects the user from the    |
|                           server                           |
|   5. clear              : Clears the chat from the window  |
|   6. Ctrl + C (client)  : Disconnects the client and       |
|                           terminates its chat session      |
|                           if present                       |
|   7. Ctrl + C (server)  : Terminates the server and all the|
|                           clients connected to the server  |
+------------------------------------------------------------+

您可以注意到|就在Username accepted行之后,这是不必要的。我无法找出问题所在。是sendMsg和receive函数有问题吗?或者没问题,我必须查看其余代码。客户端和服务器文件接近700行C++代码,所以我没有直接在这里发布。
编辑:我修改了接收函数如下:

string receive()
    {
        string msg = "";
        char last_character = '%';
        while (last_character != '|')
        {
            char buffer[2048] = {0};
            int bytes_read = read(sockfd, buffer, 2048);
            if (bytes_read <= 0)
            {
                err("Error receiving message");
                return "";
            }
            string s(buffer, bytes_read);
            for (int i = 0; i < s.size() - 1; i++)
                if (s[i] == '|')
                {
                    s.erase(s.begin() + i);
                }
            msg += s;
            last_character = s[s.size() - 1];
        }
        return msg.substr(0, msg.size() - 1);
    }
irlmq6kh

irlmq6kh1#

comm.sendMsg(string(GRN) + "Username accepted.\n" + string(NRM));

这将向套接字写入一个字符串,后跟一个尾随的|

comm.sendMsg(readme(username));

紧接着,这将写入第二个这样的字符串。

read(sockfd, buffer, 2048);

这里这个逻辑的期望是,这将最多读取一条消息,也许是部分读取。
这种期望是有缺陷的。
不管什么原因,如果服务器直到发送方向它写入了***两条***消息之后才开始read() ing套接字,那么这将把两条消息都读入buffer,并且所示的代码将完全不知道这一点。
在进行任何进一步的调试之前,需要修复此逻辑错误,因为此逻辑错误会使所有后续观察变得毫无价值。

char buffer[s.size()];
        strcpy(buffer, &s[0]);

顺便说一句:上面的内容完全不需要。s.c_str()s.size()一起,提供了write()字符串所需的一切。不需要单独的char缓冲区。

相关问题