C ++,一个接受后无法接收数据和无效套接字

时间:2016-07-09 18:46:59

标签: c++ sockets networking

我写了一个POSIX-BSD套接字[Listener-Client],我遇到了问题。我认为我的代码有问题,但我无法意识到故障在哪里。我的操作系统是Windows 10 64Bit.I在localhost上用EServer类创建一个监听器,我尝试通过ESocket连接到这个监听器。连接后,我可以将数据从ESocket发送到服务器套接字,但我无法在服务器套接字中接收它。还有,下一个传入的连接会有一个INVALID_SOCKET错误。这是我的代码:

----------------EServer.h-----------------

#ifndef ESERVER_H
#define ESERVER_H


#include <string>
#include <stdio.h>
#include <cstring>
#include <ESocket.h>

using namespace std;

#ifdef _WIN32
  #ifndef _WIN32_WINNT
    #define _WIN32_WINNT 0x0501
  #endif
  #include <winsock2.h>
  #include <Ws2tcpip.h>
#else
typedef int SOCKET;
  #include <sys/socket.h>
  #include <arpa/inet.h>
  #include <netdb.h>
  #include <unistd.h>
  #include <fcntl.h>
#endif


class EServer
{
    public:
        EServer(int port);
        virtual ~EServer();
        int Destruct();
        int Close();
        bool isListening();
        ESocket AcceptClient();

    protected:

    private:
};

#endif // ESERVER_H




----------------EServer.cpp-----------------

#include "EServer.h"
#include "stdio.h"



SOCKET sx = NULL;

EServer::EServer(int port)
{
    struct sockaddr_in server;
    //Create a socket
    if((sx = socket(AF_INET , SOCK_STREAM , 0 )) < 0)
    {
        sx = NULL;
        return;
    }



    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( port );

    //Bind
    if( bind(sx ,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
        sx = NULL;
        return;
    }

    printf("Listening status: %i\r\n",listen(sx,100));
}

EServer::~EServer()
{
    Destruct();
}


bool EServer::isListening()
{
    return (sx != NULL);
}


ESocket EServer::AcceptClient()
{
    struct sockaddr_in client;
    SOCKET new_socket;

    #ifdef _WIN32
    int c = sizeof(struct sockaddr_in);
    new_socket = accept(sx , (struct sockaddr *)&client, &c);
    if (new_socket == INVALID_SOCKET)
    {
        printf("Invalid");
    }
  #else
     socklen_t c = sizeof(struct sockaddr_in);
    new_socket = accept(sx , (struct sockaddr *)&client, &c);
    if (new_socket < 0)
    {
    }
  #endif


    return ESocket(new_socket);
}

int EServer::Destruct()
{
  #ifdef _WIN32
    return WSACleanup();
  #else
    return 0;
  #endif
}



int EServer::Close()
{


  int status = 0;
  #ifdef _WIN32
    status = shutdown(sx, SD_BOTH);
    if (status == 0) { status = closesocket(sx); }
  #else
    status = shutdown(sx, SHUT_RDWR);
    if (status == 0) { status = close(sx); }
  #endif

  return status;

}


---------------ESocket.h-------------------


#ifndef ESOCKET_H
#define ESOCKET_H

#include <string>
#include <stdio.h>
#include <cstring>

using namespace std;

#ifdef _WIN32
  #ifndef _WIN32_WINNT
    #define _WIN32_WINNT 0x0501
  #endif
  #include <winsock2.h>
#else
typedef int SOCKET;
  #include <sys/socket.h>
  #include <arpa/inet.h>
  #include <netdb.h>
  #include <unistd.h>
  #include <fcntl.h>
#endif

class ESocket
{
    public:
        ESocket(string ip,int port);
        ESocket(SOCKET e);
        virtual ~ESocket();
        int Destruct();
        int Close();
        int setAsNonBlock();
        bool SendData(string data);
        bool isConnected();

        string ReceiveData(int len);

static int Init()
{
  #ifdef _WIN32
    WSADATA wsa_data;
    return WSAStartup(MAKEWORD(1,1), &wsa_data);
  #else
    return 0;
  #endif
}

    static string getIP(char* host)
    {

    struct hostent *he;
    struct in_addr **addr_list;

    char ip[100];


    int i;

    if ( (he = gethostbyname( host ) ) == NULL)
    {
        return "";
    }

    addr_list = (struct in_addr **) he->h_addr_list;

    for(i = 0; addr_list[i] != NULL; i++)
    {

        strcpy(ip , inet_ntoa(*addr_list[i]) );
    }
    string x = ip;
    return x;
    }


    protected:

    private:

};

#endif // ESOCKET_H






--------------------ESocket.cpp---------------

#include "ESocket.h"
#include "stdio.h"

SOCKET s = NULL;


ESocket::ESocket(string ip,int port)
{
    struct sockaddr_in server;
    if((s = socket(AF_INET , SOCK_STREAM , 0 )) < 0)
    {
        s = NULL;
    }
    server.sin_addr.s_addr = inet_addr(ip.c_str());
    server.sin_family = AF_INET;
    server.sin_port = htons( port );
    if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0)
    {
        s = NULL;
    }
}

ESocket::ESocket(SOCKET e)
{
    s = e;
}

ESocket::~ESocket()
{
    Destruct();
}



bool ESocket::isConnected()
{
    return (s != NULL);
}



int ESocket::Destruct()
{
  #ifdef _WIN32
    return WSACleanup();
  #else
    return 0;
  #endif
}



int ESocket::Close()
{


  int status = 0;
  #ifdef _WIN32
    status = shutdown(s, SD_BOTH);
    if (status == 0) { status = closesocket(s); }
  #else
    status = shutdown(s, SHUT_RDWR);
    if (status == 0) { status = close(s); }
  #endif

  s = NULL;
  return status;

}


bool ESocket::SendData(string data)
{

    if( send(s , data.c_str() , data.length() , 0) < 0)
    {
        s = NULL;
        return false;
    }
    return true;
}


string ESocket::ReceiveData(int len)
{
    int recv_size;
    char reply[len];
    if((recv_size = recv(s , reply , len , 0)) < 0)
    {
        return "";
    }

    if (recv_size == 0)
    {
        s = NULL;
        return "";
    }
    printf("%i",recv_size);
    reply[recv_size] = '\0';
    return reply;
}





int ESocket::setAsNonBlock()
{

    int res;
    #ifdef _WIN32
    u_long iMode = 1;
    res = ioctlsocket(s, FIONBIO, &iMode);
    #else
    int opts;
    opts = fcntl(s, F_GETFL);
    if(opts < 0)
    {
        res = -1;
    }
    opts = (opts | O_NONBLOCK);
    if(fcntl(s, F_SETFL, opts) < 0)
    {
        res = -1;
    }
    #endif
    return res;
}



 -------------MAIN--------------

ESocket::Init();


EServer e(105);
while (e.isListening()){
ESocket x1("127.0.0.1",105);
printf("Connecting\n");
ESocket x2 = e.AcceptClient();
printf("Accepted\n");
x1.SendData("Ehem");
printf("Sent\n");
cout<<x2.ReceiveData(100);
printf("Received\n");
    Sleep(2000);
}

如果有人能向我解释我该如何解决,我会很高兴。

1 个答案:

答案 0 :(得分:3)

问题是您对所有连接的套接字使用单个全局变量。当您接受套接字时,将单个套接字分配给最新接受的套接字。当接受新的套接字连接时,您将使用新套接字覆盖单个变量,从而丢失先前的连接。

您应该在类中将这些全局变量设为私有成员变量。通过这样做,SOCKET变量对于每个ESocket实例的每个实例都是唯一的。

还有一些风格可以改进。我在评论中提到了一个。另一个是变量名ssx?不是很具描述性,我想你选择了sx,因为你不能在两个源文件中使用s?这应该是你做错事的标志。

最后是他们最重要的缺陷:你在破坏者的脚下拉着地毯。

析构函数调用WSACleanup,它真正清理整个winsocket子系统,以及链接的WSACLeanup引用中的单词:

  

调用WSACleanup时打开的套接字将被重置并自动释放,就像调用closesocket一样。

这是非常有问题的,因为EServer::AcceptClient按值返回新创建的套接字,这会导致创建临时实例 并销毁 。临时对象的这种破坏是导致其套接字子系统关闭的原因。从那一点开始的任何套接字操作都将导致错误。

退出程序时,您应该只调用WSACleanup一次。

如果析构函数应该执行任何操作,则应该关闭套接字,但这会导致其他问题,因为您不遵循rule of three, five or zero。你绝对应该遵循这里的rule of three,并实现一个复制构造函数和复制赋值运算符。