我写了一个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);
}
如果有人能向我解释我该如何解决,我会很高兴。
答案 0 :(得分:3)
问题是您对所有连接的套接字使用单个全局变量。当您接受套接字时,将单个套接字分配给最新接受的套接字。当接受新的套接字连接时,您将使用新套接字覆盖单个变量,从而丢失先前的连接。
您应该在类中将这些全局变量设为私有成员变量。通过这样做,SOCKET
变量对于每个ESocket
实例的每个实例都是唯一的。
还有一些风格可以改进。我在评论中提到了一个。另一个是变量名s
和sx
?不是很具描述性,我想你选择了sx
,因为你不能在两个源文件中使用s
?这应该是你做错事的标志。
最后是他们最重要的缺陷:你在破坏者的脚下拉着地毯。
析构函数调用WSACleanup
,它真正清理整个winsocket子系统,以及链接的WSACLeanup
引用中的单词:
调用
WSACleanup
时打开的套接字将被重置并自动释放,就像调用closesocket
一样。
这是非常有问题的,因为EServer::AcceptClient
按值返回新创建的套接字,这会导致创建临时实例 并销毁 。临时对象的这种破坏是导致其套接字子系统关闭的原因。从那一点开始的任何套接字操作都将导致错误。
退出程序时,您应该只调用WSACleanup
一次。
如果析构函数应该执行任何操作,则应该关闭套接字,但这会导致其他问题,因为您不遵循rule of three, five or zero。你绝对应该遵循这里的rule of three,并实现一个复制构造函数和复制赋值运算符。