我回来了一个新问题。我必须使用OOP创建新版本的同步聊天服务器。我将套接字存储在std :: map中,其中键是客户端的名称,值是ip :: tcp :: socket的共享指针。由于我这样做,我遇到了以下问题:
我启动服务器和几个客户端。他们成功地互相写信。但是,如果我关闭其中一个连接,服务器会以这种方式中止:
server: /sapmnt/HOME/i322722/usr/boost/include/boost/smart_ptr/shared_ptr.hpp:687:
typename boost::detail::sp_member_access<T>::type
boost::shared_ptr<T>::operator->() const [with T =
boost::asio::basic_stream_socket<boost::asio::ip::tcp>; typename
boost::detail::sp_member_access<T>::type =
boost::asio::basic_stream_socket<boost::asio::ip::tcp>*]: Assertion `px != 0' failed.
当我读到断言失败时,它是关于未初始化的共享指针的东西。但我在代码中找不到类似的东西。这是代码:
server.h:
#ifndef SERVER_H_INCLUDED__
#define SERVER_H_INCLUDED__
#include<string>
#include<boost/asio.hpp>
#include<boost/asio/ip/tcp.hpp>
#include "common.h"
typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr;
class Server
{
boost::asio::io_service& service;
boost::asio::ip::tcp::acceptor acceptor;
boost::mutex mtx;
public:
Server(boost::asio::io_service& serv);
void start();
private:
void identify(socket_ptr sock);
void writeMessage(const std::string& clientName, std::string& message);
void notification(const std::string& clientName, const std::string headOfMsg, const std::string tailOfMsg);
void disconnectClient(const std::string& clientName);
bool readMessage(socket_ptr sock, std::string& message);
void processLoop(const std::string& clientName);
};
#endif
server.cpp:
#include<iostream>
#include<list>
#include<boost/thread.hpp>
#include<boost/bind.hpp>
#include<boost/asio.hpp>
#include<boost/asio/ip/tcp.hpp>
#include<boost/interprocess/smart_ptr/unique_ptr.hpp>
#include "common.h"
#include "server.h"
using namespace std;
using namespace boost::asio;
using namespace boost::asio::ip;
const string waitingMsg("Waiting for clients...\n");
const string totalClientsMsg("Total clients: ");
const int EOF_ERROR_CODE = 2;
map <string, socket_ptr> clientMap;
Server::Server(io_service& serv) : service(serv), acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), PORT_NO))
{
}
void Server::start()
{
cout << waitingMsg;
while (true)
{
socket_ptr sock(new tcp::socket(service));
boost::system::error_code error;
acceptor.accept(*sock, error);
if (error)
{
cerr << "Error on accepting: " << error.message() << endl;
cout << waitingMsg;
continue;
}
boost::shared_ptr <boost::thread> p (new boost::thread(boost::bind(&Server::identify, this, sock)));
}
}
void Server::writeMessage(const string& clientName, string& message)
{
mtx.lock();
boost::system::error_code error;
message.append(1, '\n');
for(auto& cliSock : clientMap)
{
if (cliSock.second->is_open() && cliSock.first != clientName)
{
cliSock.second->write_some(buffer(message), error);
if (error)
{
cerr << errorWritingMsg << error.message() << endl;
}
}
}
mtx.unlock();
}
void Server::notification(const string& clientName, const string headOfMsg, const string tailOfMsg)
{
string serviceMsg = headOfMsg + clientName + tailOfMsg;
cout << serviceMsg << totalClientsMsg << clientMap.size() << endl;
writeMessage(clientName, serviceMsg);
cout << waitingMsg;
}
void Server::disconnectClient(const string& clientName)
{
mtx.lock();
boost::system::error_code error;
clientMap[clientName]->shutdown(tcp::socket::shutdown_both, error);
if (error)
{
cerr << "Error on shutting: " << error.message() << endl;
}
clientMap[clientName]->close(error);
if(error)
{
cerr << "Error on closing: " << error.message() << endl;
}
clientMap.erase(clientName);
mtx.unlock();
notification(clientName, "", " disconnected. ");
}
bool Server::readMessage(socket_ptr sock, string& message)
{
mtx.lock();
boost::asio::streambuf buff;
boost::system::error_code error;
size_t bytes_transferred = boost::asio::read_until(*sock, buff, '\n', error);
if(error)
{
if (error.value() != EOF_ERROR_CODE)
{
cerr << errorReadingMsg << error.message() << endl;
}
return false;
}
buff.commit(bytes_transferred);
istream istrm(&buff);
getline(istrm, message);
mtx.unlock();
if(message + "\n" == "exit\n")
{
return false;
}
return true;
}
void Server::processLoop(const string& clientName)
{
while (true)
{
{
string message = "";
if (!readMessage(clientMap[clientName], message))
{
disconnectClient(clientName);
return;
}
message.insert(0, clientName + ": ");
writeMessage(clientName, message);
}
}
}
void Server::identify(socket_ptr sock)
{
mtx.lock();
sock->write_some(buffer("Please, enter your name:\n"));
mtx.unlock();
string name = "";
boost::system::error_code error;
bool occupied;
do
{
if (!readMessage(sock, name))
{
return;
}
occupied = false;
if (clientMap.find(name) != clientMap.end())
{
occupied = true;
}
if (occupied)
{
mtx.lock();
sock->write_some(buffer("This name is already in use! Please, enter another name:\n"), error);
mtx.unlock();
if (error)
{
cerr << errorWritingMsg << error.message() << endl;
}
}
}
while (occupied);
mtx.lock();
clientMap.emplace(name, sock);
mtx.unlock();
notification(name, "New client: ", " joined. ");
processLoop(name);
}
client.cpp:
#include<iostream>
#include<boost/thread.hpp>
#include<boost/bind.hpp>
#include<boost/asio.hpp>
#include<boost/asio/ip/tcp.hpp>
#include<boost/algorithm/string.hpp>
#include "common.h"
using namespace std;
using namespace boost::asio;
using namespace boost::asio::ip;
io_service service;
tcp::endpoint ep(ip::address::from_string(IP), PORT_NO);
void displayLoop(socket_ptr sock)
{
while(true)
{
{
boost::asio::streambuf buff;
boost::system::error_code error;
size_t bytes_transferred = boost::asio::read_until(*sock, buff, '\n', error);
if (error)
{
cerr << errorReadingMsg << error.message() << endl;
continue;
}
buff.commit(bytes_transferred);
istream istrm(&buff);
string message = "";
getline(istrm, message);
buff.consume(buff.size());
cout << message << endl;
}
}
}
void writeLoop(socket_ptr sock)
{
string message = "";
while(true)
{
getline(cin, message);
message += '\n';
boost::system::error_code error;
sock->write_some(buffer(message), error);
if (error)
{
cerr << errorWritingMsg << error.message() << endl;
continue;
}
if(message == "exit\n")
{
exit(0);
}
message.clear();
}
}
int main(int argc, char* argv[])
{
boost::thread_group threads;
socket_ptr sock(new tcp::socket(service));
boost::system::error_code error;
sock->connect(ep, error);
if (error)
{
cerr << "Error on connecting: " << error.message() << endl;
return -1;
}
cout << "Type \"exit\" to quit.\n";
threads.create_thread(boost::bind(displayLoop, sock));
threads.create_thread(boost::bind(writeLoop, sock));
threads.join_all();
return 0;
}
COMMON.H:
#ifndef COMMON_H_INCLUDED__
#define COMMON_H_INCLUDED__
#include<string>
typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr;
const unsigned PORT_NO = 30001;
const std::string IP = "127.0.0.1";
const std::string errorWritingMsg("Error on writing: ");
const std::string errorReadingMsg("Error on reading: ");
const std::string exitLower = "exit\n";
const std::string exitCapInit = "Exit\n";
const std::string exitCaps = "EXIT\n";
#endif
serverMain.cpp:
#include<boost/asio.hpp>
#include "common.h"
#include "server.h"
using namespace std;
using namespace boost::asio;
using namespace boost::asio::ip;
int main(int argc, char *argv[])
{
boost::asio::io_service service;
Server server(service);
server.start();
return 0;
}
生成文件:
all: serverMain client
libraries = -lboost_thread -lboost_system
server.o: server.cpp
g++ -I/sapmnt/HOME/i322722/usr/boost/include/ $(libraries) -L/sapmnt/HOME/i322722/usr/boost/lib/ -c server.cpp -std=c++11 -lrt -ggdb
serverMain.o: serverMain.cpp
g++ -I/sapmnt/HOME/i322722/usr/boost/include/ $(libraries) -L/sapmnt/HOME/i322722/usr/boost/lib/ -c serverMain.cpp -std=c++11 -lrt -ggdb
client.o: client.cpp
g++ -I/sapmnt/HOME/i322722/usr/boost/include/ $(libraries) -L/sapmnt/HOME/i322722/usr/boost/lib/ -c client.cpp -std=c++11 -lrt
server.o: server.cpp
g++ -c server.cpp -lpthread $(libraries) -L/sapmnt/HOME/i322722/usr/boost/lib -std=c++11 -lrt -ggdb
serverMain: serverMain.o server.o
g++ -o serverMain serverMain.o server.o -lpthread $(libraries) -L/sapmnt/HOME/i322722/usr/boost/lib -std=c++11 -lrt -ggdb
client: client.o
g++ -o client client.o -lpthread $(libraries) -L/sapmnt/HOME/i322722/usr/boost/lib -std=c++11 -lrt
clean:
rm *.o serverMain client
每次关闭连接时都不会出现问题(有时候如果我想关闭第一个建立的连接,那就没问题,但下一个连接失败了),但大多数时候我都有。我想在加入新客户时已多次出现,但我对此并不是100%肯定。
我认为问题与std :: map有关,更确切地说,与函数
有关Server::disconnectClient(...)
server.cpp 中的但我不确定。在介绍这个结构之前,一切都运行良好,但是对于套接字和名称存在分离的std :: lists,这很糟糕。我不知道问题的确切位置以及如何解决问题。各种帮助将不胜感激。
答案 0 :(得分:0)
我真的很抱歉浪费你的时间。今天我意识到发生了这样一个非常愚蠢的事情。程序的第一个版本仅包含server.cpp和client.cpp(当然还包括makefile)。在makefile中,我编译服务器和客户端,导致执行文件“server”和“client”。由于我切换到OOP版本,makefile生成的新执行文件是“serverMain”和“client”。但是,直到今天早上我仍然一直在调用“服务器”,所以它一直是旧的非OOP版本,给出了奇怪的断言失败。
我会保留代码,以防其他错误发生。非常感谢您的帮助。