简体   繁体   English

简单的非阻塞多线程tcp服务器

[英]Simple non-blocking multi-threaded tcp server

I'm studying C++, and this weekend I started to play around with sockets and threads. 我正在学习C ++,本周末我开始使用套接字和线程。 Bellow is a simple multi threaded server that I'm making based on some tutorials. Bellow是一个简单的多线程服务器,我根据一些教程制作。

The issue that I'm facing is that when I'm connecting with 2 telnet clients only the keystrokes form the first connection appear on the server. 我面临的问题是,当我与2个telnet客户端连接时,只有第一个连接的击键出现在服务器上。 Any keystroke sent from the second telnet connection appears suddenly once the first telnet connection closes. 第一个telnet连接关闭后,第二个telnet连接发送的任何键击突然出现。 Could someone explain to me what have I done wrong here? 有人可以向我解释我在这里做错了什么吗?

#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;

}

This should work. 这应该工作。 I removed pointless things like setting the socket to non-blocking and disabling the Nagle algorithm. 我删除了无意义的事情,例如将套接字设置为非阻塞并禁用Nagle算法。 The latter should only be done for things that need low-millisecond interactivity. 后者应该仅用于需要低毫秒交互性的事物。

But, the substantial change that should fix your problem is changing join to detach . 但是,应该解决您的问题的重大变化是将join更改为detach Using join causes your program to wait for the thread to finish before continuing. 使用join会导致程序在继续之前等待线程完成。 Using detach says "This thread is going to run in the background doing things, and I don't care about learning its fate later.". 使用detach说“这个线程将在后台运行,我不关心以后学习它的命运。”

If you don't use one of the two, and the ::std::thread object is destroyed, the system throws an exception because you're destroying the only means you have of getting information about whether or not a thread exited with an error of some kind with saying that either you don't care about such information, or explicitly asking for it. 如果你不使用其中一个,并且::std::thread对象被销毁,那么系统会抛出一个异常,因为你正在破坏你获取有关一个线程是否退出的唯一方法。某种错误,说你不关心这些信息,或明确要求它。

I don't have Windows, so I can't test it: 我没有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;

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM