服务器和客户端套接字连接问题重新。send(), accept() 和多线程
Server & client socket connection issue re. send(), accept() and multi-threading
我正在用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;
}
我明白了!客户端不是多线程的原因是,创建客户端连接的程序是在互斥对象中创建的——因此,在旧连接收到服务器的回复之前,它不会创建新连接,因此服务器看起来只是单线程的!所以简而言之,我上面的服务器程序是好的,这是一个问题在客户端-抱歉浪费你的时间-我甚至没有考虑到这种可能性,直到我完全重做了程序结构,把线程放在客户端,然后揭示了这个问题。
谢谢你的帮助!
您的套接字阻塞!这意味着它们将等待操作完成后再返回。
这是如何使套接字非阻塞:
bool nonblock(int sock)
{
int flags;
flags = fcntl(sock, F_GETFL, 0);
flags |= O_NONBLOCK;
return (fcntl(sock, F_SETFL, flags) == 0);
}
现在函数accept
, read
和write
都将返回一个错误,如果套接字将阻塞,设置errno
变量为EWOULDBLOCK
或EAGAIN
。
如果你想等待一个套接字准备好读写,你可以使用select
函数。对于侦听套接字(您执行accept
的套接字),当可以接受新连接时,它将准备好读取。
- 通过套接字[TCP]传输数据 如何在C / C ++中打包多个整数并使用send() recv()传输数据
- 当对套接字 send() 的同步调用由于连接另一端丢失而被阻止时,如何恢复?
- ZMQ::send() 抛出异常并终止 QNX 进程.为什么以及如何从中恢复?
- Visual accept std::string from std::byte iterator
- 在UNIX中通过recv/send交换数据时,如何正确使用缓冲区
- 如何为winsock的send()函数构造constchar*缓冲区
- 实时线程中的 ZeroMQ inproc PubSub send() 调用会导致严重的阻塞吗?
- accept(..) 似乎正在修改我给它的文件描述符参数
- Epoll zero recv() and negative(EAGAIN) send()
- 为什么我们需要在 Visitor 模式中 accept(),为什么我们不能直接调用 visitor.visit()?
- QDBus send int[]
- WinSock c++ inet_ntop始终显示 204.204.204.204(并且 accept() 没有失败)
- 如何在 TCP/IP 服务器中中断 accept()?
- send() 或 recv() 未同步
- 使用SEND()之前,请使用Select()检查套接字
- MFC 对话框控件的"Accept Files"选项如何工作?
- shutdown() 和 closesocket() 在 SOCKET 上的 send() 之后不久
- 对'cocos2d::network::HttpClient::send(cocos2d::network::HttpRequest*)'的未定义引用
- accept() 块(带有挂起的连接);Ctrl+C 解封?
- 服务器和客户端套接字连接问题重新。send(), accept() 和多线程