使用线程运行服务器时的确切操作顺序

时间:2015-03-24 11:05:49

标签: c++ multithreading

我需要在c ++中创建一个服务器,它可以响应多个客户,从而使用线程。

这是我的类Socket,实际的通信完成了:

typedef struct addrinfo addinfo;

class Socket
{
 private:
     WSADATA _wsaData;
     int _iResult;
     SOCKET _ListenSocket;
     addrinfo* _result;
     addrinfo _hints;
     int _iSendResult;
     char _recvbuf[BUF_LEN];
     int _recvbuflen;
     std::vector<SOCKET> _ClientSockets;

 public:
     Socket();
     bool Setup();
     bool CreateSocket();
     bool Bind();
     bool Listen();
     bool Accept();
     bool Send();
     bool Shutdown();
     ~Socket();
 };

Socket::Socket()
{
    _ListenSocket = INVALID_SOCKET;
    _ClientSockets.push_back(INVALID_SOCKET);
    _result = NULL;
    _recvbuflen = BUF_LEN;
}


bool Socket::Setup() {
    _iResult = WSAStartup(MAKEWORD(2, 2), &_wsaData);
    if (_iResult != 0) {
        std::cout << "WSAStartup failed with error: " << _iResult << std::endl;
        return false;
    }

    ZeroMemory(&_hints, sizeof(_hints));
    _hints.ai_family = AF_INET;
    _hints.ai_socktype = SOCK_STREAM;
    _hints.ai_protocol = IPPROTO_TCP;
    _hints.ai_flags = AI_PASSIVE;

    _iResult = getaddrinfo(NULL, DEFAULT_PORT, &_hints, &_result);
    if (_iResult != 0) {
        std::cout << "getaddrinfo failed with error: " << _iResult << std::endl;
        WSACleanup();
        return false;
    }

    return true;
}

bool Socket::CreateSocket() {
    _ListenSocket = socket(_result->ai_family, _result->ai_socktype, _result->ai_protocol);
    if (_ListenSocket == INVALID_SOCKET) {
        std::cout << "socket failed with error: " << WSAGetLastError() << std::endl;
        freeaddrinfo(_result);
        WSACleanup();
        return false;
    }
    return true;
}

bool Socket::Bind() {
    _iResult = bind(_ListenSocket, _result->ai_addr, (int)_result->ai_addrlen);
    if (_iResult == SOCKET_ERROR) {
        std::cout << "bind failed with error: " << WSAGetLastError() << std::endl;
        freeaddrinfo(_result);
        closesocket(_ListenSocket);
        WSACleanup();
        return false;
    }

    freeaddrinfo(_result);
    return true;
}

bool Socket::Listen() {
    _iResult = listen(_ListenSocket, SOMAXCONN);
    if (_iResult == SOCKET_ERROR) {
        std::cout << "listen failed with error: " << WSAGetLastError() << std::endl;
        closesocket(_ListenSocket);
        WSACleanup();
        return false;
    }

    return true;
}

bool Socket::Accept() {
    _ClientSockets.push_back(accept(_ListenSocket, NULL, NULL));
    if (_ClientSockets[_ClientSockets.size() - 1] == INVALID_SOCKET) {
        std::cout << "accept failed with error: " << WSAGetLastError() << std::endl;
        closesocket(_ListenSocket);
        WSACleanup();
        return false;
    }

    return true;
}

bool Socket::Send() {
    closesocket(_ListenSocket);
    _iResult = recv(_ClientSockets[_ClientSockets.size() - 1], _recvbuf, _recvbuflen, 0);

    if (_iResult > 0) {
        _iSendResult = send(_ClientSockets[_ClientSockets.size() - 1], "Accepted", strlen("Accepted"), 0);
        if (_iSendResult == SOCKET_ERROR) {
            std::cout << "send failed with error: " << WSAGetLastError() << std::endl;
            closesocket(_ClientSockets[_ClientSockets.size() - 1]);
            WSACleanup();
            return false;
        }
    }
    else if (_iResult == 0)
        std::cout << "Connection closing..." << std::endl;
    else  {
        std::cout << "recv failed with error: " << WSAGetLastError() << std::endl;
        closesocket(_ClientSockets[_ClientSockets.size() - 1]);
        WSACleanup();
        return false;
    }

    return true;
}

bool Socket::Shutdown() {
    _iResult = shutdown(_ClientSockets[_ClientSockets.size() - 1], SD_SEND);
    if (_iResult == SOCKET_ERROR) {
        std::cout << "shutdown failed with error: " << WSAGetLastError() << std::endl;
        closesocket(_ClientSockets[_ClientSockets.size() - 1]);
        WSACleanup();
        return false;
    }

    closesocket(_ClientSockets[_ClientSockets.size() - 1]);
    WSACleanup();

    return true;
}

Socket::~Socket()
{
}

这是主文件:

void interact(Socket* socket);

int main() {
    Socket* socket = new Socket();
    socket->Setup();
    socket->CreateSocket();
    socket->Bind();
    socket->Listen();
    while (1) {
        socket->Accept();
        std::thread trd(interact, socket);
        trd.join();
        system("PAUSE");
    }
    socket->Shutdown();
    system("PAUSE");
    return 0;
}

void interact(Socket* socket) {

    std::cout << "Accepted" << std::endl;
    socket->Send();
}

与第一个客户端进行通信很好,但在我尝试回到监听并完成与最后一个客户端的通信之后,它给出了错误代码10038 for accept和10093 for recv。我无法理解错误代码在我的代码中实际意味着什么。我怀疑它可能与我使用Accept的方式有关。是应该在交互功能中,还是在我调用线程之前?我能指点一下吗?

1 个答案:

答案 0 :(得分:1)

所以你的有效控制流程是:

while (1) {
    socket->Accept();   // uses socket->_ListenSocket
    interact(socket);   // calls socket->Send()
        socket->Send(); // calls closesocket(_ListenSocket);

既然您故意关闭了侦听套接字,为什么您希望后续的呼叫接受工作呢?


其他说明:

  1. 您将整个服务器套接字传递给每个客户端线程,这远远超出了它的需要。每个客户端任务应该只需要自己的客户端套接字,然后就不会像在这里那样损坏服务器套接字。
  2. 您目前正在通过加入线程来序列化所有内容,但如果您想要并行,则需要将其分离。这将进一步打破每个线程的现有模型,从Socket的数组中获取最后一个客户端套接字(没有同步)。
  3. 拥有一个用于处理任务的线程池通常是一个更好的主意,并使用自己的套接字使每个任务成为客户端请求。这样你就不会一直创建和销毁线程,它会迫使你思考什么构成了一个独立的任务。