Я разрабатываю серверную программу на C ++ для получения нескольких клиентских подключений и передачи их в потоки, однако я зашел в тупик.
Все соединения с сокетами работают нормально, как и многопоточность- почти.Пожалуйста, посмотрите мой код ниже (он компилируется и работает нормально).
Я попытался свести его к основам, чтобы вы могли легко следить за ним и занимать меньше всего времени.Я прокомментировал код, чтобы помочь вам увидеть, где проблема, а затем опишу проблему подробно внизу.Если вы можете мне помочь, я был бы очень признателен!
#include <vector>
#include <boost/thread.hpp>
#include "unix_serverSocket.h"
#include "server.h"
extern const string socketAddress;
void do_stuff(ServerSocket *client)
{
string in;
string out;
try
{
/* Gets input until the client closes the connection, then throws an exception, breaking out of the loop */
while (true)
{
*client >> in; /* Receives data from client socket connection */
/* Assume the input is processed fine and returns the result into 'out' */
sleep(3); /* I've put sleep() here to test it's multithreading properly - it isn't */
*client << out; /* Returns result to client - send() is called here */
/* If I put sleep() here instead it multithreads fine, so the server is waiting for send() before it accepts a new client */
}
}
catch (SocketException &)
{
delete client;
return;
}
}
int main()
{
try
{
ServerSocket server(socketAddress);
while (true)
{
ServerSocket *client = new ServerSocket();
/* See below */
server.accept(*client);
boost::thread newThread(do_stuff, client);
}
}
catch (SocketException &e)
{
cout << "Error: " << e.description() << endl;
}
return 0;
}
После того, как клиентское сокетное соединение было передано потоку, main () возвращается к строке:
server.accept(*client);
но затем ждет, пока предыдущее соединение отправит свой результат обратно клиенту через send (), прежде чем оно примет новое соединение - т.е. сервер ожидает, что что-то произойдет в потоке, прежде чем он примет нового клиента!Я не хочу, чтобы это делалось - я хочу, чтобы он отправлял клиентское соединение потоку, а затем сразу принимал больше клиентских соединений и передавал их в другие потоки!
Если вам интересно, почему я создал указатель на сокет здесь ...
ServerSocket *client = new ServerSocket();
... если я не создаю указатель, тогда функция recv ()вызываемый потоком не может получить данные от клиента, что, по-видимому, связано с тем, что поток поверхностно копирует соединение с сокетом клиента, а сборщик мусора не понимает потоки и думает, что клиентское соединение больше не будет использоваться после того, как оно прошлов поток и, таким образом, уничтожая его перед вызовом recv () в потоке.Следовательно, используя указатель, созданный в куче, которая работала.В любом случае, когда я переработал код, используя fork () вместо потоков (что означало, что мне не нужно было создавать сокет в куче), у меня все еще была та же проблема с сервером, который не мог принимать новых клиентов.
Полагаю, мне нужно как-то изменить настройки сервера, чтобы клиент не ожидал отправки () перед тем, как принять новый, однако, несмотря на большое количество Google, я все еще в растерянности!
Вот соответствующий код подключения сокетов на случай, если это поможет (сервер и клиенты находятся в одном блоке и, следовательно, подключаются через локальные сокеты UNIX):
class Socket
{
private:
int sockfd;
struct sockaddr_un local;
public:
Socket();
virtual ~Socket();
bool create();
bool bind(const string &);
bool listen() const;
bool accept(Socket &) const;
bool send(const string &) const;
int recv(string &) const;
void close();
bool is_valid() const
{
return sockfd != -1;
}
};
bool Socket::create()
{
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (!is_valid())
{
return false;
}
int reuseAddress = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuseAddress, sizeof(reuseAddress)) == -1)
{
return false;
}
return true;
}
bool Socket::bind(const string &socketAddress)
{
if (!is_valid())
{
return false;
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, socketAddress.c_str());
unlink(local.sun_path);
int len = strlen(local.sun_path) + sizeof(local.sun_family);
int bind_return = ::bind(sockfd, (struct sockaddr *) &local, len);
if (bind_return == -1)
{
return false;
}
return true;
}
bool Socket::listen() const
{
if (!is_valid())
{
return false;
}
int listen_return = ::listen(sockfd, MAXCLIENTCONNECTIONS);
if (listen_return == -1)
{
return false;
}
return true;
}
bool Socket::accept(Socket &socket) const
{
int addr_length = sizeof(local);
socket.sockfd = ::accept(sockfd, (sockaddr *) &local, (socklen_t *) &addr_length);
if (socket.sockfd <= 0)
{
return false;
}
else
{
return true;
}
}
int Socket::recv(string &str) const
{
char buf[MAXRECV + 1];
str = "";
memset(buf, 0, MAXRECV + 1);
int status = ::recv(sockfd, buf, MAXRECV, 0);
if (status == -1)
{
cout << "status == -1 errno == " << errno << " in Socket::recv" << endl;
return 0;
}
else if (status == 0)
{
return 0;
}
else
{
str = buf;
return status;
}
}
bool Socket::send(const string &str) const
{
int status = ::send(sockfd, str.c_str(), str.size(), MSG_NOSIGNAL);
if (status == -1)
{
return false;
}
else
{
return true;
}
}
class ServerSocket : private Socket
{
public:
ServerSocket(const string &);
ServerSocket() {};
virtual ~ServerSocket();
void accept(ServerSocket &);
const ServerSocket & operator << (const string &) const;
const ServerSocket & operator >> (string &) const;
};
ServerSocket::ServerSocket(const string &socketAddress)
{
if (!Socket::create())
{
throw SocketException("Could not create server socket");
}
if (!Socket::bind(socketAddress))
{
throw SocketException("Could not bind to port");
}
if (!Socket::listen())
{
throw SocketException("Could not listen to socket");
}
}
void ServerSocket::accept(ServerSocket &socket)
{
if (!Socket::accept(socket))
{
throw SocketException("Could not accept socket");
}
}
const ServerSocket & ServerSocket::operator << (const string &str) const
{
if (!Socket::send(str))
{
throw SocketException("Could not write to socket");
}
return *this;
}
const ServerSocket & ServerSocket::operator >> (string &str) const
{
if (!Socket::recv(str))
{
throw SocketException("Could not read from socket");
}
return *this;
}