我在用C++编程的套接字应用程序遇到麻烦。我正在使用Windows XP上的Bloodshed Dev-Cpp进行编程。我创建了一个用于处理所有消息传输的类,并拥有一个客户端和服务器程序,它们都使用该类来处理其服务。该应用程序本身非常基础,我唯一的目的就是使所有这些工作正常进行。

用于发送消息的客户端,其工作方式与我期望的一样。如果我的服务器正在运行,则发送消息时没有任何错误。如果它没有运行,将传递一个错误。但是我的服务器不断接受奇怪的胡言乱语。总是相同的数据。当它收到消息时没有任何作用。如果我让我的客户尝试识别服务器,则它会变得乱七八糟。

我在这里包括了我的源代码。链接器还引入了两个额外的参数:-lwsock32和Dev-Cpp随附的库libws2_32.a。

这是我的Messager类的标题:

#ifndef MESSAGER
#define MESSAGER

#include <string>

class Messager{
    private:
        int sendSocket;
        int listenSocket;

    public:
        void init(void);
        bool connect(std::string ip, std::string port);
        bool bind(std::string port);
        void listen(void);
        void send(std::string message);
        std::string receive(void);
};
#endif

这些是我对Messager类的定义:
#include "Messager.h"
#include <winsock2.h>
#include <sys/types.h>
#include <ws2tcpip.h>
#include <windows.h>

void Messager::init(void){
    WSADATA wsaData;

    WSAStartup(MAKEWORD(1,1), &wsaData);
}

bool Messager::connect(std::string ip, std::string port){
    struct addrinfo hints;
    struct addrinfo *res;
    bool success = false;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    getaddrinfo(ip.c_str(), port.c_str(), &hints, &res);

    sendSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

    success = ::connect(sendSocket, res->ai_addr, res->ai_addrlen) != -1;

    freeaddrinfo(res);

    return success;
}

bool Messager::bind(std::string port){
    struct addrinfo hints, *res;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    getaddrinfo(NULL, port.c_str(), &hints, &res);

    listenSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

    if(listenSocket == INVALID_SOCKET){
        return false;
    }

    if(::bind(listenSocket, res->ai_addr, res->ai_addrlen) == -1){
        return false;
    }

    return true;
}

void Messager::listen(void){
    ::listen(listenSocket, 10);
}

int Messager::send(std::string message){
    const std::string terminator = "\r\n";
    std::string realMessage;
    int size = 0;
    int totalSent = 0;

    realMessage = message;
    realMessage += terminator;

    size = realMessage.size();

    totalSent = ::send(sendSocket, realMessage.c_str(), size, 0);

    if(totalSent == 0 || totalSent == -1){
        return 0; // There must be an error, 0 means it is an error
    }

    // This statement keeps adding the results of ::send to totalSent until it's the size of the full message
    for(totalSent = 0; totalSent < size; totalSent += ::send(sendSocket, realMessage.c_str(), size, 0));

    return totalSent;
}

// This function has been updated a lot thanks to @Luke
std::string Messager::receive(void){
    const int bufferSize = 256;
    const std::string terminator = "\r\n";
    char buffer[bufferSize];
    int i = 0;
    int received = 0;
    std::string tempString;
    size_t term = 0;

    for(i = 0; i < bufferSize; i++){
        buffer[i] = 0;
    }

    received = ::recv(listenSocket, buffer, bufferSize, 0);
    tempString = buffer;
    term = tempString.find(terminator);

    if(term != -1){ // Already have line
        line = tempString;
    }

    while(received != -1 && received != 0){ // While it is receiving information...
        // Flush the buffer
        for(i = 0; i < bufferSize; i++){
            buffer[i] = 0;
        }

        ::recv(listenSocket, buffer, bufferSize, 0);
        tempString += buffer;
        term = tempString.find(terminator);

        if(term != -1){ // Found terminator!
            return tempString;
        }
    }

    throw 0; // Didn't receive any information.  Throw an error
}

关于可能发生的事情的任何想法将不胜感激。如有必要,我可以发布服务器和客户端使用的代码,但是我可以给出一个大致的概述:

服务器:
  • messager.init()
  • messager.bind()
  • messager.listen()
  • messager.receive()
    客户:
  • messager.init()
  • messager.connect()
  • messager.send()

  • 提前致谢。

    最佳答案

    我看到两个问题。

  • 您不能在Message::receive()中安全地使用字符串赋值运算符。赋值运算符依赖于以NULL结尾的字符数组,在这种情况下不是。这可能是用大量垃圾数据填充的。您应该获取实际接收到的字符数(即recv()的返回值),并使用string::assign()方法填充字符串对象。
  • 没有代码可确保已发送或接收所有数据。 recv()将在任何数据可用时立即返回;您确实需要循环播放,直到收到完整的消息为止。对于纯文本数据,通常人们使用CR-LF对来指示行尾。您继续调用recv()并缓冲结果,直到看到该CR-LF对,然后将该行返回给调用方。您还应该循环发送send(),直到发送完整个缓冲区为止。

  • 通常,这看起来像以下内容(全部来自内存,因此可能存在一些小错误,但这只是要点):
    bool Message::Receive(std::string& line)
    {
        // look for the terminating pair in the buffer
        size_t term = m_buffer.find("\r\n");
        if(term != -1)
        {
            // already have a line in the buffer
            line.assign(m_buffer, 0, term); // copy the line from the buffer
            m_buffer.erase(0, term + 2); // remove the line from the buffer
            return true;
        }
        // no terminating pair in the buffer; receive some data over the wire
        char tmp[256];
        int count = recv(m_socket, tmp, 256);
        while(count != -1 && count != 0)
        {
            // successfully received some data; buffer it
            m_buffer.append(tmp, count);
            // see if there is now a terminating pair in the buffer
            term = m_buffer.find("\r\n");
            if(term != -1)
            {
                // we now have a line in the buffer
                line.assign(m_buffer, 0, term); // copy the line from the buffer
                m_buffer.erase(0, term + 2); // remove the line from the buffer
                return true;
            }
            // we still don't have a line in the buffer; receive some more data
            count = recv(m_socket, tmp, 256);
        }
        // failed to receive data; return failure
        return false;
    }
    

    关于c++ - Winsock recv()和accept()的问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1960038/

    10-12 03:59