我正在用 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() 而不是线程(这意味着我不需要在堆上创建套接字)重新编写代码时,我仍然遇到服务器无法接受新客户端的相同问题。
我想我需要以某种方式更改服务器设置,以便它在接受新客户端之前不会等待客户端发送(),但是尽管谷歌搜索了很多,我仍然不知所措!
这里是相关的套接字连接代码,以防有帮助(服务器和客户端都在同一个盒子上,因此通过本地 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;
}
最佳答案
我知道了!客户端不是多线程的原因是创建客户端连接的程序是在一个互斥体中这样做的——因此它不会创建一个新连接,直到旧连接收到服务器的回复,因此服务器似乎只能是单线程!所以简而言之,我上面的服务器程序很好,但它是客户端的问题 - 抱歉浪费你的时间 - 我什至没有考虑这种可能性,直到我通过将线程放在客户端来完全重新设计程序结构,然后揭示了问题。
感谢您的帮助!
关于c++ - 服务器和客户端套接字连接问题重新。 send()、accept() 和多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7930101/