winsock recv()和accept()的问题

时间:2009-12-24 23:20:25

标签: c++ sockets winsock

我在使用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()&lt; - 包括accept()

客户端:

  • messager.init()
  • messager.connect()
  • messager.send()

提前致谢。

4 个答案:

答案 0 :(得分:3)

我看到两个问题。

  1. 您无法安全地在Message :: receive()中使用字符串赋值运算符。赋值运算符依赖于以NULL结尾的字符数组,在这种情况下它不是。它可能充满了一堆垃圾数据。您应该获得实际接收的字符数(即recv()的返回值)并使用string :: assign()方法填充字符串对象。
  2. 没有代码可以确保已发送或接收所有数据。一旦有任何数据,recv()将立即返回;你真的需要循环,直到你收到整个消息。对于纯文本数据,通常人们使用CR-LF对来指示行的结尾。你继续调用recv()并缓冲结果,直到你看到CR-LF对,然后将该行返回给调用者。您还应该在send()上循环,直到发送完整个缓冲区为止。
  3. 通常这看起来类似于以下内容(这些都来自内存所以可能存在一些小错误,但这是它的要点):

    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;
    }
    

答案 1 :(得分:1)

我有点担心你的receive代码。它创建一个char*缓冲区来接收数据,但实际上并没有为它分配任何内存。

现在我无法判断你是否在那里调用WinSock recv,因为你没有明确说::recv,但我认为你需要:

  • 首先用malloc分配一些空格(id recv想要一个缓冲区);或
  • 传递缓冲区指针的地址(如果recv分配自己的缓冲区)。

我真的很惊讶不会导致核心转储,因为当你致电buffer时,recv的值可以设置为任何

这样的事可能会更好:

char *Messager::receive(void){
    int newSocket = 0;
    struct sockaddr_storage *senderAddress;
    socklen_t addressSize;
    char *buffer;

    addressSize = sizeof senderAddress;
    newSocket = accept(listenSocket, (struct sockaddr *)&senderAddress,
        &addressSize);

    buffer = new char[20];
    recv(newSocket, buffer, 20, 0);

    return buffer;

}

但是你需要记住,这个函数的客户端负责在缓冲区完成后释放缓冲区。

答案 2 :(得分:1)

两个建议:

  • 检查您调用的所有套接字函数的返回值
  • 沟渠DevC ++ - 这是一个多病的地狱&amp;已不再开发 - 请改用http://www.codeblocks.org/

答案 3 :(得分:0)

receive函数中,buffer本地永远不会初始化为任何内容,因此您最终会将消息读入某些随机内存,并可能导致损坏或崩溃。您可能需要char buffer[MAX_MSG_LENGTH];而不是char *buffer