2009-12-24 2 views
2

C++로 프로그래밍중인 소켓 응용 프로그램에 문제가 있습니다. Windows XP에서 Bloodshed Dev-Cpp로 프로그래밍하고 있습니다. 모든 메시지 전송을 처리하기위한 클래스를 만들었고 클라이언트와 서버 프로그램 모두에서 해당 클래스를 사용하여 서비스를 처리했습니다. 응용 프로그램 자체는 매우 기본적인 것입니다.이 모든 작업을 수행하는 것이 유일한 목적입니다.winsock recv() 및 accept() 문제

메시지를 보내는 클라이언트는 예상대로 작동합니다. 내 서버가 실행 중이면 메시지를 보낼 때 오류가 발생하지 않습니다. 실행 중이 아니면 오류가 발생합니다. 그러나 제 서버는 계속 이상한 횡설수설을 받아들입니다. 항상 같은 데이터입니다. 메시지를 받으면 효과가 없습니다. 내 고객이 서버를 식별하려고하면 다시 횡설수설합니다.

여기에 내 소스 코드가 포함되었습니다. 링커는 또한 두 개의 추가 매개 변수를 제공합니다. -lwsock32 및 Dev-Cpp와 함께 제공되는 libws2_32.a 라이브러리 포함.

여기 내 멧세이 클래스의 헤더입니다 : 정말 감사하겠습니다에 갈 수있는 일에 대해

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

어떤 아이디어 :

#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.init()
  • messager.bind()
  • 멧세이을 들어 봐요()
  • messager.receive() <는 -) (수용 포함

클라이언트 :

  • messager.init()
  • messager.connect()
  • messager.send() 사전에

감사합니다.

답변

1

receive 코드에 대해 조금 걱정이됩니다. 데이터를 수신하기 위해 char* 버퍼를 생성하지만 실제 메모리를 할당하지는 않습니다. 이제

난 당신이 명시 적으로 ::recv 말을하지 않기 때문에 당신이 거기 윈속 recv를 호출하고 있는지 알 수 없지만 나도에 당신이 필요로하는 것이라고 생각 :

  • malloc 첫 번째 (ID로 공간을 할당 recv은 버퍼를 원합니다); 또는
  • 은 버퍼 포인터의 주소를 전달합니다 (recv이 자체 버퍼를 할당하는 경우).

난 당신이 recv를 호출 할 때 buffer의 값이 아무것도로 설정 될 수 있기 때문에이 코어 덤프가 발생하지 않는 것을 실제로 놀랐어요. 이 같은

뭔가 더 좋을 수 있습니다

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; 

}

하지만이 기능의 클라이언트가 그것으로 완료 때 버퍼를 확보 할 책임이 있음을 기억해야합니다.

+0

내가 그것에 대해 걱정했다. 나는 버퍼 룸을주기 위해 100 개 정도의 요소를 배열하려고 시도했지만 문제는 남아 있습니다. 다시 테스트하고 코드를 편집하여 문제의 일부가 아닌지 확인합니다. – rovaughn

+0

방금 ​​테스트를했는데 동일한 결과가 나타났습니다. 나는 그것을 안전하게 배열로 유지할 것이다. 의견을 보내 주셔서 감사합니다. – rovaughn

+0

함수 배열을 벗어나지 못하기 때문에 char 배열로 만들 수 없습니다. 함수가 끝나고 반환 한 포인터가 쓰레기를 가리키면 파기됩니다. 데이터 영역을 malloc해야합니다. – paxdiablo

1

두 가지 제안 :

  • 검사가 모든 소켓 함수의 반환 값은 당신 ++
  • 도랑 DevC를 호출하지 - 지옥 & 더 이상 개발되고있다으로는 버그가 - 대신 http://www.codeblocks.org/를 사용합니다.
+0

'socket == SOCKET_INVALID' (또는 그 효과가있는 것)인지 확인 하시겠습니까? 일할 수있어, 나는 그것을 시도 할 것이다. 컴파일러 또는 IDE의 버그가 무엇입니까? 그러나 더 업데이트 된 라이브러리 등이 있으면 코드 블록이 좋을 것입니다. 나는 그것을 시도 할 것이다, 고마워. – rovaughn

+0

저는 Microsoft의 Visual Studio Express를 다운로드하는 것에 대해 진지한 생각을했습니다. 그것은 무료이며 (말하기가 아닌 맥주가없는) 훨씬 더 잘 지원됩니다. – paxdiablo

+0

나는 그것을 시험해 보았지만 나는 그것을별로 좋아하지 않는다. 어쩌면 내 뒤에서 느낄 수 있기 때문에 .dll과 다른 것들에 의존하게 만들 것입니다. 나는 기대하지 않습니다. 나는 틀릴 수 있었다. – rovaughn

0

receive 함수에서는 buffer 로컬이 초기화되지 않으므로 임의의 메모리에 메시지가 읽히고 부패 또는 충돌이 발생할 수 있습니다. 아마도 char *buffer 대신 char buffer[MAX_MSG_LENGTH];이 필요합니다.

+0

실제로 작동하지 않습니다. @Chris. 버퍼가 함수가 종료 될 때 실행되지 않습니다. malloc이 필요합니다. – paxdiablo

+0

버퍼가 반환되는 문자열을 초기화하는 데 사용되기 때문에 괜찮을 것입니다. 그 후에는 사라질 수 있습니다. –

3

두 가지 문제점이 있습니다.

  1. 안전하게 문자 배열 할당 연산자를 Message :: receive()에 사용할 수 없습니다. 할당 연산자는 문자 배열이 NULL로 끝나는 것에 의존하며,이 경우에는 그렇지 않습니다. 아마도 쓰레기 데이터가 가득 차있을 것입니다. 실제로받은 문자 수 (즉, recv()의 반환 값)를 가져와 string :: assign() 메서드를 사용하여 문자열 객체를 채 웁니다.
  2. 모든 데이터를 보내고 받았는지 확인하는 코드는 없습니다. 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; 
} 
+0

입니다. 아마도 많은 것들을 도와 줄 수있는 감사합니다. 완충 대가 초기화되지 않았기 때문에 버퍼가 쓰레기로 채워질 것이라고 생각했지만 현재는 원했던 것을 반환하기를 원했기 때문에 고칠 예정 이었지만 고맙습니다. 하지만 당신이 send()와 recv() 메시지를 나누는 문제는 아마도이 모든 것의 근원이라고 생각합니다. 대단히 감사합니다. 아마도 이것은 답 일 것입니다. – rovaughn

+0

그로 인해 문제가 발생했지만 원래의 문제는 여전히 남아 있습니다. send가 내가 원하는 모든 것을 보내지 않고있는 것을 보았습니다. 그리고 위에서 볼 수 있듯이, 나는 그 문제를 해결했습니다. 고마워, 루크. 하지만 recv()는 여전히 이상한 횡설수설을하고 있으며 나는 왜 그런지 모릅니다. send()는 문제가있는 메시지를 연결하고 보낼 수 있음을 보여 주며 소켓은 유효하지만 recv()는 여전히 횡설수설합니다. – rovaughn