简单的非阻塞多线程tcp服务器
Simple non-blocking multi-threaded tcp server
我正在学习 C++,这个周末我开始研究套接字和线程。 Bellow 是我根据一些教程制作的一个简单的多线程服务器。
我面临的问题是,当我与 2 个 telnet 客户端连接时,服务器上只显示第一个连接的击键。一旦第一个 telnet 连接关闭,从第二个 telnet 连接发送的任何击键都会突然出现。有人可以向我解释我在这里做错了什么吗?
#include <iostream>
#include <string>
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "ws2_32.lib")
void clientSocketHandler(SOCKET clientSocket, std::string client_ip) {
char buf[4096];
std::thread::id thread_id = std::this_thread::get_id();
std::cout << thread_id << " - " << client_ip << ": connected" << std::endl;
while (true)
{
ZeroMemory(buf, 4096);
int bytesReceived = recv(clientSocket, buf, 4096, 0);
if (bytesReceived == 0)
{
std::cout << thread_id << " - " << client_ip << ": disconnected" << std::endl;
break;
}
if (bytesReceived > 0)
{
std::cout << thread_id << " - " << client_ip << ": " << std::string(buf, 0, bytesReceived) << std::endl;
//send(clientSocket, buf, bytesReceived + 1, 0);
}
}
std::cout << thread_id << " - " << client_ip << ": closing client socket & exiting thread..." << std::endl;
closesocket(clientSocket);
}
void waitForConnections(SOCKET serverSocket) {
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(1337);
hint.sin_addr.S_un.S_addr = INADDR_ANY;
bind(serverSocket, (sockaddr*)&hint, sizeof(hint));
listen(serverSocket, SOMAXCONN);
while (true) {
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&client, &clientSize);
if (clientSocket != INVALID_SOCKET)
{
char host[NI_MAXHOST]; // Client's remote name
ZeroMemory(host, NI_MAXHOST); // same as memset(host, 0, NI_MAXHOST);
std::string client_ip = inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::thread t(clientSocketHandler, clientSocket, client_ip);
t.join();
}
Sleep(100);
}
}
int main()
{
// Initialze winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
std::cerr << "Can't Initialize winsock! Quitting..." << std::endl;
return 1;
}
// Create a socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
WSACleanup();
std::cerr << "Can't create a socket! Quitting..." << std::endl;
return 1;
}
// If serverSocketMode = 0, blocking is enabled;
// If serverSocketMode != 0, non-blocking mode is enabled.
u_long serverSocketMode = 1;
if (ioctlsocket(serverSocket, FIONBIO, &serverSocketMode) != NO_ERROR)
{
WSACleanup();
std::cerr << "Can't set socket to non-blocking mode! Quitting..." << std::endl;
return 1;
}
// Disables the Nagle algorithm for send coalescing.
// This socket option is included for backward
// compatibility with Windows Sockets 1.1
BOOL flag = TRUE;
if (setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&flag, sizeof(flag)) != NO_ERROR)
{
WSACleanup();
std::cerr << "Can't set socket NO_DELAY option! Quitting..." << std::endl;
return 1;
}
// Start listening for connections
waitForConnections(serverSocket);
// Cleanup winsock
WSACleanup();
system("pause");
return 0;
}
这应该有效。我删除了一些毫无意义的事情,比如将套接字设置为 non-blocking 并禁用 Nagle 算法。后者应该只用于需要 low-millisecond 交互性的事情。
但是,解决您的问题的实质性更改是将 join
更改为 detach
。使用 join
会导致您的程序在继续之前等待线程完成。使用 detach
表示 "This thread is going to run in the background doing things, and I don't care about learning its fate later.".
如果您不使用两者之一,并且 ::std::thread
对象被销毁,系统将抛出异常,因为您正在销毁获取有关是否线程退出时出现某种错误,表示您不关心此类信息,或者明确要求此类信息。
我没有Windows,所以无法测试:
#include <iostream>
#include <string>
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "ws2_32.lib")
void clientSocketHandler(SOCKET clientSocket, std::string client_ip)
{
char buf[4096];
std::thread::id thread_id = std::this_thread::get_id();
std::cout << thread_id << " - " << client_ip << ": connected" << std::endl;
while (true)
{
ZeroMemory(buf, 4096);
int bytesReceived = recv(clientSocket, buf, 4096, 0);
if (bytesReceived == 0)
{
std::cout << thread_id << " - " << client_ip << ": disconnected" << std::endl;
break;
}
if (bytesReceived > 0)
{
std::cout << thread_id << " - " << client_ip << ": " << std::string(buf, 0, bytesReceived) << std::endl;
//send(clientSocket, buf, bytesReceived + 1, 0);
}
}
std::cout << thread_id << " - " << client_ip << ": closing client socket & exiting thread..." << std::endl;
closesocket(clientSocket);
}
void waitForConnections(SOCKET serverSocket)
{
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(1337);
hint.sin_addr.S_un.S_addr = INADDR_ANY;
bind(serverSocket, (sockaddr*)&hint, sizeof(hint));
listen(serverSocket, SOMAXCONN);
while (true) {
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&client, &clientSize);
if (clientSocket != INVALID_SOCKET)
{
char host[NI_MAXHOST]; // Client's remote name
ZeroMemory(host, NI_MAXHOST); // same as memset(host, 0, NI_MAXHOST);
std::string client_ip = inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::thread t(clientSocketHandler, clientSocket, client_ip);
t.detach();
}
Sleep(100);
}
}
int main()
{
// Initialze winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
std::cerr << "Can't Initialize winsock! Quitting..." << std::endl;
return 1;
}
// Create a socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
WSACleanup();
std::cerr << "Can't create a socket! Quitting..." << std::endl;
return 1;
}
// Start listening for connections
waitForConnections(serverSocket);
// Cleanup winsock
WSACleanup();
system("pause");
return 0;
}
我正在学习 C++,这个周末我开始研究套接字和线程。 Bellow 是我根据一些教程制作的一个简单的多线程服务器。
我面临的问题是,当我与 2 个 telnet 客户端连接时,服务器上只显示第一个连接的击键。一旦第一个 telnet 连接关闭,从第二个 telnet 连接发送的任何击键都会突然出现。有人可以向我解释我在这里做错了什么吗?
#include <iostream>
#include <string>
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "ws2_32.lib")
void clientSocketHandler(SOCKET clientSocket, std::string client_ip) {
char buf[4096];
std::thread::id thread_id = std::this_thread::get_id();
std::cout << thread_id << " - " << client_ip << ": connected" << std::endl;
while (true)
{
ZeroMemory(buf, 4096);
int bytesReceived = recv(clientSocket, buf, 4096, 0);
if (bytesReceived == 0)
{
std::cout << thread_id << " - " << client_ip << ": disconnected" << std::endl;
break;
}
if (bytesReceived > 0)
{
std::cout << thread_id << " - " << client_ip << ": " << std::string(buf, 0, bytesReceived) << std::endl;
//send(clientSocket, buf, bytesReceived + 1, 0);
}
}
std::cout << thread_id << " - " << client_ip << ": closing client socket & exiting thread..." << std::endl;
closesocket(clientSocket);
}
void waitForConnections(SOCKET serverSocket) {
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(1337);
hint.sin_addr.S_un.S_addr = INADDR_ANY;
bind(serverSocket, (sockaddr*)&hint, sizeof(hint));
listen(serverSocket, SOMAXCONN);
while (true) {
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&client, &clientSize);
if (clientSocket != INVALID_SOCKET)
{
char host[NI_MAXHOST]; // Client's remote name
ZeroMemory(host, NI_MAXHOST); // same as memset(host, 0, NI_MAXHOST);
std::string client_ip = inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::thread t(clientSocketHandler, clientSocket, client_ip);
t.join();
}
Sleep(100);
}
}
int main()
{
// Initialze winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
std::cerr << "Can't Initialize winsock! Quitting..." << std::endl;
return 1;
}
// Create a socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
WSACleanup();
std::cerr << "Can't create a socket! Quitting..." << std::endl;
return 1;
}
// If serverSocketMode = 0, blocking is enabled;
// If serverSocketMode != 0, non-blocking mode is enabled.
u_long serverSocketMode = 1;
if (ioctlsocket(serverSocket, FIONBIO, &serverSocketMode) != NO_ERROR)
{
WSACleanup();
std::cerr << "Can't set socket to non-blocking mode! Quitting..." << std::endl;
return 1;
}
// Disables the Nagle algorithm for send coalescing.
// This socket option is included for backward
// compatibility with Windows Sockets 1.1
BOOL flag = TRUE;
if (setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&flag, sizeof(flag)) != NO_ERROR)
{
WSACleanup();
std::cerr << "Can't set socket NO_DELAY option! Quitting..." << std::endl;
return 1;
}
// Start listening for connections
waitForConnections(serverSocket);
// Cleanup winsock
WSACleanup();
system("pause");
return 0;
}
这应该有效。我删除了一些毫无意义的事情,比如将套接字设置为 non-blocking 并禁用 Nagle 算法。后者应该只用于需要 low-millisecond 交互性的事情。
但是,解决您的问题的实质性更改是将 join
更改为 detach
。使用 join
会导致您的程序在继续之前等待线程完成。使用 detach
表示 "This thread is going to run in the background doing things, and I don't care about learning its fate later.".
如果您不使用两者之一,并且 ::std::thread
对象被销毁,系统将抛出异常,因为您正在销毁获取有关是否线程退出时出现某种错误,表示您不关心此类信息,或者明确要求此类信息。
我没有Windows,所以无法测试:
#include <iostream>
#include <string>
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "ws2_32.lib")
void clientSocketHandler(SOCKET clientSocket, std::string client_ip)
{
char buf[4096];
std::thread::id thread_id = std::this_thread::get_id();
std::cout << thread_id << " - " << client_ip << ": connected" << std::endl;
while (true)
{
ZeroMemory(buf, 4096);
int bytesReceived = recv(clientSocket, buf, 4096, 0);
if (bytesReceived == 0)
{
std::cout << thread_id << " - " << client_ip << ": disconnected" << std::endl;
break;
}
if (bytesReceived > 0)
{
std::cout << thread_id << " - " << client_ip << ": " << std::string(buf, 0, bytesReceived) << std::endl;
//send(clientSocket, buf, bytesReceived + 1, 0);
}
}
std::cout << thread_id << " - " << client_ip << ": closing client socket & exiting thread..." << std::endl;
closesocket(clientSocket);
}
void waitForConnections(SOCKET serverSocket)
{
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(1337);
hint.sin_addr.S_un.S_addr = INADDR_ANY;
bind(serverSocket, (sockaddr*)&hint, sizeof(hint));
listen(serverSocket, SOMAXCONN);
while (true) {
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&client, &clientSize);
if (clientSocket != INVALID_SOCKET)
{
char host[NI_MAXHOST]; // Client's remote name
ZeroMemory(host, NI_MAXHOST); // same as memset(host, 0, NI_MAXHOST);
std::string client_ip = inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::thread t(clientSocketHandler, clientSocket, client_ip);
t.detach();
}
Sleep(100);
}
}
int main()
{
// Initialze winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
std::cerr << "Can't Initialize winsock! Quitting..." << std::endl;
return 1;
}
// Create a socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
WSACleanup();
std::cerr << "Can't create a socket! Quitting..." << std::endl;
return 1;
}
// Start listening for connections
waitForConnections(serverSocket);
// Cleanup winsock
WSACleanup();
system("pause");
return 0;
}