1

我正在通过 tcp 实现 ping-pong。我用 C# 编写了 Pinger。还有两根手指。一个用 C# 编写,另一个用 C++ 编写。Pinger 只是向pongers 发送消息,而pongers 回复他。问题是当 C# ponger 工作时,一切正常,但是当 C++ ponger 工作时,Pinger 的线路

 var res = client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                            new AsyncCallback(ReceiveCallback), state);

不运行回调(ReceiveCallback)。并且 ReceiveCallback 仅被调用一次(来自 Receive 函数)。pinger 代码片段在这里:

  private void Receive(Socket client)
    {
        try
        {
            // Create the state object.
            StateObject state = new StateObject();
            state.workSocket = client;

            // Begin receiving the data from the remote device.
            client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReceiveCallback), state);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        } finally
        {
        }
    }
    private void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // Retrieve the state object and the client socket 
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.workSocket;

            int bytesRead = 0;
            try
            {
                // Read data from the remote device.
                bytesRead = client.EndReceive(ar);
            }
            catch (ObjectDisposedException e)
            {
                Console.WriteLine(client.Connected);
                if (_isDown)
                    return;
                else
                {
                    throw e;
                }
            }

            if (bytesRead > 0)
            {
                // There might be more data, so store the data received so far.
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

                try
                {
                    // Get the rest of the data.

                    var res = client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                        new AsyncCallback(ReceiveCallback), state);
                }
                catch (ObjectDisposedException e)
                {
                    if (_isDown)
                        return;
                    else
                    {
                        throw e;
                    }
                }
            }
            else
            {
                // All the data has arrived; put it in _response.
                if (state.sb.Length > 1)
                {
                    _response = state.sb.ToString();
                }
                // Signal that all bytes have been received.
                _receiveDone.Set();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

C# Ponger 在这里:

  public void AsyncListenFor()
        {
            // Bind the socket to the local endpoint and listen for incoming connections.
            while (_doPong)
            {
                try
                {
                    _localEndPoint = new IPEndPoint(_ipAddress, _port);
                    if (!_listener.IsBound)
                    {
                        _listener = CreateSocket(_listener);
                        BindAndListen(_listener, _localEndPoint);
                    }
                    // Set the event to nonsignaled state.
                    allDone.Reset();
                    // Start an asynchronous socket to listen for connections.
                    //Console.WriteLine("Waiting for a connection...");
                    _listener.Blocking = true;
                    _listener.BeginAccept(new AsyncCallback(AcceptCallback), _listener);
                    // Wait until a connection is made before continuing.
                    allDone.WaitOne();
                }
                catch (Exception e)
                {
                    // здесь не будем показывать эксепшон WatchDog`у чтобы не перезагружать прогу
                    //(если автостартер не будет работать)
                    if (_listener != null)
                    {
                        try
                        {
                           //_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                            //Socket dataSocket = AsyncSocket.EndAccept(_IAsyncResult);
                            _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 0));
                            _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
                        }
                        catch (Exception e2)
                        { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
                        try
                        {
                            _listener.Shutdown(SocketShutdown.Both);
                        }
                        catch (Exception e2)
                        { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
                        try
                        {
                            _listener.Disconnect(false);
                        }
                        catch (Exception e2)
                        { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
                        try
                        {
                            _listener.Close();
                        }
                        catch (Exception e2)
                        { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
                    }
                    CLogger.WriteLog(CLogger.ELogLevel.ERROR, "");
                    CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e.Message + "\n" + e.StackTrace);
                    Thread.Sleep(1000);
                }
            }
        }
        public Socket CreateSocket(Socket listener)
        {
            if (listener != null)
            {
                try
                {
                    listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 0));
                    listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
                }
                catch (Exception e2)
                { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
                try
                {
                    _listener.Shutdown(SocketShutdown.Both);
                }
                catch (Exception e2)
                { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
                try
                {
                    _listener.Disconnect(false);
                }
                catch (Exception e2)
                { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
                try
                {
                    _listener.Close();
                }
                catch (Exception e2)
                { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
            }
            try
            {
                listener = new Socket(AddressFamily.InterNetwork,
                                      SocketType.Stream, ProtocolType.Tcp);
                listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 0));
                listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
            } catch (Exception e)
            {
                CLogger.WriteLog(CLogger.ELogLevel.ERROR, "Не смогли создать сокет:\t" + e.Message);
            }
            return listener;
        }
        public void BindAndListen(Socket listener, IPEndPoint localEndPoint)
        {
            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(100);
            } catch (Exception e)
            { CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e.Message + "\n" + e.StackTrace); }
        }
        public void AcceptCallback(IAsyncResult ar)
        {
            // Signal the main thread to continue.
            allDone.Set();
            // Get the socket that handles the client request.
            Socket listener = (Socket)ar.AsyncState;
            Socket handler = listener.EndAccept(ar);

            // Create the state object.
            StateObject state = new StateObject();
            state.workSocket = handler;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                 new AsyncCallback(ReadCallback), state);
        }
        public void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            // Retrieve the state object and the handler socket
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.workSocket;

            // Read data from the client socket. 
            int bytesRead = handler.EndReceive(ar);

            if (bytesRead > 0)
            {
                // There  might be more data, so store the data received so far.
                state.sb.Append(Encoding.ASCII.GetString(
                    state.buffer, 0, bytesRead));

                // Check for end-of-file tag. If it is not there, read 
                // more data.
                content = state.sb.ToString();
                if (content.IndexOf("<EOF>") > -1)
                {
                    // All the data has been read from the 
                    // client. Display it on the console.
                    //Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
                    //                  content.Length, content);
                    // Echo the data back to the client.
                    Send(handler, _progName);
                }
                else
                {
                    // Not all data received. Get more.
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                         new AsyncCallback(ReadCallback), state);
                }
            }
        }
        private void Send(Socket handler, String data)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device.
            handler.BeginSend(byteData, 0, byteData.Length, 0,
                              new AsyncCallback(SendCallback), handler);
        }
        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket handler = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.
                int bytesSent = handler.EndSend(ar);
                //Console.WriteLine("Sent {0} bytes to client.", bytesSent);

                handler.Shutdown(SocketShutdown.Both);
                handler.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

C++ Ponger 在这里:

// listen the sotket and send 
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
while(true) {
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
     if (iResult != 0) {
    LOG(logINFO) << "Could not initialize Winsock";
    continue;
}
    ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
  // Create a SOCKET for connecting to server
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
    LOG(logINFO) << "Could not create socket.";
    WSACleanup();
    continue;
}
    // Setup the TCP listening socket
    struct sockaddr_in client;
    client.sin_family = AF_INET;
    client.sin_port = htons(port_);
    client.sin_addr.s_addr = inet_addr(ip_.c_str());
iResult = bind(ListenSocket, (struct sockaddr *)&client, sizeof(struct sockaddr_in));
if (iResult == SOCKET_ERROR) {
     LOG(logINFO) << "Bind failed.";
    freeaddrinfo(result);
    closesocket(ListenSocket);
    WSACleanup();
    continue;
}
    iResult = listen(ListenSocket, 0);
if (iResult == SOCKET_ERROR) {
    LOG(logINFO) << "listen failed";
    closesocket(ListenSocket);
    WSACleanup();
    continue;
}
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
    LOG(logINFO) << "accept failed";
    closesocket(ListenSocket);
    WSACleanup();
    continue;
}
    closesocket(ListenSocket);
    // Receive until the peer shuts down the connection
    do {
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            // Echo the buffer back to the sender
            iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                continue;
            }
            printf("Bytes sent: %d\n", iSendResult);
        }
        else if (iResult == 0)
            printf("Connection closing...\n");
        else  {
            LOG(logINFO) << "recv failed";
            closesocket(ClientSocket);
            WSACleanup();
            continue;
        }
    } while (iResult > 0);
}
4

1 回答 1

1

除非我遗漏了什么,否则在您的 C++ 服务器中,您实际上并没有关闭客户端的套接字。

在 recv 返回 0 表示正常断开连接的情况下,我们进入else if (iResult == 0)分支。然后我们打印“连接关闭...”并退出do ... while循环。我们实际上没有在任何地方调用shutdownor closesocket

你的程序也有一个非传统的结构。通常像这样的简单服务器如下所示:

create the listen socket
bind the listen socket
listen
while (!needToShutdown)
    accept
    read client request
    do work
    send response
    shutdown client socket
    close client socket
// we are now shutting down
close listen socket

请注意,这里我们在接受客户端后不会关闭侦听套接字。我们可以使用同一个监听套接字一个接一个地接受多个客户端。维基百科有一个例子。(让这个服务同时处理多个客户端太复杂了,这里不讨论。)

于 2013-05-17T17:55:38.043 回答