【发布时间】:2022-01-13 10:33:44
【问题描述】:
我试图将我的服务器套接字分离为一个单例。代码如下:
ServerSocket.h
#pragma once
#include <asio.hpp>
#include <iostream>
using asio::ip::tcp;
class ServerSocket
{
public:
ServerSocket(ServerSocket& otherSingleton) = delete;
void operator=(const ServerSocket& copySingleton) = delete;
tcp::acceptor* InitAcceptor();
tcp::socket* InitSocket();
void StartServerSocket();
void SendData(std::string);
std::array<char, 5000> RecieveData();
static ServerSocket* GetInstance();
private:
static ServerSocket* instance;
tcp::acceptor* acceptor;
tcp::socket* socket;
asio::io_context io_context;
ServerSocket() {
acceptor = InitAcceptor();
socket = InitSocket();
}
~ServerSocket()
{
std::cout << "Server closed";
}
};
ServerSocket.cpp
#include "ServerSocket.h"
tcp::acceptor* ServerSocket::InitAcceptor()
{
try
{
tcp::acceptor* acceptor = new tcp::acceptor(io_context, tcp::endpoint(tcp::v4(), 27015));
return acceptor;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
tcp::socket* ServerSocket::InitSocket()
{
try
{
tcp::socket* socket = new tcp::socket(io_context);
return socket;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void ServerSocket::StartServerSocket()
{
try
{
std::cout << "Server started";
for (;;)
{
acceptor->accept(*socket);
};
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
std::array<char, 5000> ServerSocket::RecieveData()
{
try {
std::array<char, 5000> buf;
asio::error_code error;
size_t len = socket->read_some(asio::buffer(buf), error);
buf[len] = '\0';
return buf;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
ServerSocket* ServerSocket::instance(nullptr);
ServerSocket* ServerSocket::GetInstance()
{
if (instance == nullptr)
{
instance = new ServerSocket();
}
return instance;
}
服务器套接字启动,我得到:
Server started
当客户端连接时,我得到:
accept: Already open
然后服务器停止。
我认为错误来自接受器在 for 函数中。但根据文档,它应该以这种方式工作。 (或者至少我是这么理解的 - https://think-async.com/Asio/asio-1.20.0/doc/asio/tutorial/tutdaytime2.html)
我尝试删除 for 循环,如下所示:
try
{
std::cout << "Server started";
acceptor->accept(*socket);
}
现在没有问题了。但是服务器没有保持连接打开。客户端连接一次,发送数据,服务器停止运行。
据我从文档中了解到,如果我在 for(;;) 中设置接受器,它应该正在运行 - 但在我的情况下它不起作用。
那么,我怎样才能在我的实现中保持我的套接字打开?我希望它运行多个 SendData - 只要客户端连接,我希望它能够与客户端通信。
谢谢。
//编辑:
这是客户端代码:
#include <iostream>
#include <asio.hpp>
#include "../../cereal/archives/json.hpp"
using asio::ip::tcp;
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: client <host>" << std::endl;
return 1;
}
// Socket Parameters
const unsigned port = 27015;
auto ip_address = asio::ip::make_address_v4(argv[1]);
auto endpoint = tcp::endpoint{ ip_address, port };
// Creating and Connecting the Socket
asio::io_context io_context;
auto resolver = tcp::resolver{ io_context };
auto endpoints = resolver.resolve(endpoint);
auto socket = tcp::socket{ io_context };
asio::connect(socket, endpoints);
std::array<char, 5000> buf;
std::cout << "Message to server: ";
asio::error_code ignored_error;
std::string username = "test", password = "mihai";
std::stringstream os;
{
cereal::JSONOutputArchive archive_out(os);
archive_out(
CEREAL_NVP(username),
CEREAL_NVP(password)
);
}
asio::write(socket, asio::buffer(os.str()), ignored_error);
return 0;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
return 1;
}
而 Communication.h 负责从客户端捕获操作并将其发送到服务器
#pragma once
#include <iostream>
#include "DBUser.h"
#include "DBPost.h"
class Communication
{
public:
enum class Operations {
eLogin,
eRegister
};
void ExecuteOperation(Operations operation,const std::array<char, 5000>& buffer);
};
.cpp
#include "Communication.h"
void Communication::ExecuteOperation(Operations operation,const std::array<char, 5000>& buffer)
{
DBUser* user= DBUser::getInstance();
switch (operation)
{
case Communication::Operations::eLogin:
{
std::string username, password;
std::stringstream is(buffer.data());
{
cereal::JSONInputArchive archive_in(is);
archive_in(username,password);
}
try
{
user->LoginUser(username, password);
}
catch (const std::exception& e)
{
std::cout << e.what();
}
break;
}
case Communication::Operations::eRegister:
{
std::string username, password;
std::stringstream is(buffer.data());
{
cereal::JSONInputArchive archive_in(is);
archive_in(username, password);
}
try
{
user->CreateUser(username, password);
}
catch (const std::exception& e)
{
std::cout << e.what();
}
break;
}
}
}
主要
#include <iostream>
#include <pqxx/pqxx>
#include "DBLink.h"
#include "DBUser.h"
#include "DBPost.h"
#include "../Logging/Logging.h"
#include <iostream>
#include <string>
#include <asio.hpp>
#include "ServerSocket.h"
#include "Communication.h"
int main()
{
ServerSocket* test = ServerSocket::GetInstance();
test->StartServerSocket();
std::array<char, 5000> buf = test->RecieveData();
Communication communicationInterface;
communicationInterface.ExecuteOperation(Communication::Operations::eRegister, buf);
system("pause");
}
【问题讨论】:
-
我认为如果你使用异步而不是同步,你想做的事情会更容易。很难想象一个单线程同步服务器正在侦听新连接并同时读取客户端消息。我也看不到你在哪里读到的东西?
-
@KoronisNeilos 我没有包括该部分,因为问题来自服务器 - 所以客户端部分不是必需的。 (在我看来)
-
如果您希望服务器从客户端读取消息,您需要在服务器上调用 read
-
您可以发布一个工作示例并解释您想要做什么吗?你想写一个同步服务器,它接受一个客户端并打印客户端发送的所有内容吗?
-
为了清楚起见,您的
for loop在*socket上调用accept,然后——没有关闭*socket——再次调用acceptor->accept(*socket)。因此,您在已处于连接状态的套接字描述符上调用accept- 因此出现错误。请注意,您的代码与link you posted 中的代码完全不同。
标签: c++ boost boost-asio