TCP无法从客户端重新连接到服务器

时间:2016-07-01 22:35:38

标签: c++ sockets tcp connect

我正在为我的代码库编写TCP通信库。我已经用一个acceptor类(在服务器上运行)和连接器类(在客户端上运行)和一个流类(处理发送和接收数据)实现了这个库。

在大多数情况下,一切正常。我遇到的一个问题是,如果我断开客户端和服务器之间的以太网线路,并尝试连接,我会收到目标无法访问的错误。在进一步尝试连接时,无论是否连接了以太网电缆,我都会收到操作正在进行的错误。

请注意,如果调用connect,即使重新连接,一切都可以正常连接。

另外,请注意我在连接期间的代码中将套接字选项设置为SO_REUSEADDR,我使用的是SO_LINGER,并且在连接失败后我将关闭所有套接字。

TCPConnector类:

/** @file    TCPConnector.cpp
 *  @brief   cpp file to TCPConnector class, which encapsulates the socket mechanisms to actively
 *           connect to a server.
 *  @author  Austin Small.
 */

#include "TCPConnector.h"
#include <iostream>
#include <errno.h>


/** @brief   This method establishes a connection with the server (robot).
 *
 *  @param   server     Server IP address.
 *  @param   port       Server port number.
 *  @param   timeoutSec Number of seconds before timout of connect method.
 */

TCPStream* TCPConnector::connect(const char* serverIP, int port, int timeoutSec)
{
    std::cout << "connect was called" << std::endl;

    struct sockaddr_in address;

    // Store all zeros for address struct.
    memset(&address, 0, sizeof(address));

    // Configure address struct.
    address.sin_family = AF_INET;
    address.sin_port = htons(port);                    // Convert from host to TCP network byte order.
    inet_pton(PF_INET, serverIP, &(address.sin_addr)); // Convert IP address to network byte order.

    // Create a socket.  The socket signature is as follows: socket(int domain, int type, int protocol)
    int sd = socket(AF_INET, SOCK_STREAM, 0);

    int optval = 1;

    if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) == -1)
    {
        std::cout << "failed to set socket option" << std::endl;
    }

    // Set socket to terminate all communications when close is called.
    struct linger so_linger;

    so_linger.l_onoff  = true;
    so_linger.l_linger = 0;

    if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger) == -1)
    {
        std::cout << "failed to set socket option" << std::endl;
    }



    // Set socket to be non-blocking.
    int arg;
    arg  = fcntl(sd, F_GETFL, NULL);
    arg |= O_NONBLOCK;
    fcntl(sd, F_SETFL, arg);

    // Connect with time limit.
    fd_set set;
    FD_ZERO(&set);    // Clear the set.
    FD_SET(sd, &set); // Add our file descriptor to the set.

    struct timeval timeout;
    timeout.tv_sec  = timeoutSec;
    timeout.tv_usec = 0;

    // If the connect call returns 0, then the connection was established.  Otherwise,
    // check if the three-way handshake is underway.
    if (::connect(sd, (struct sockaddr *)&address, sizeof(address)) < 0)
    {
        // If the handshake is underway.
        if (errno == EINPROGRESS)
        {
            std::cout << "handshake in progress" << std::endl;


            // Designate timeout period.
            int ret = select(sd + 1, NULL, &set, NULL, &timeout);

            std::cout << "return value from select : " << ret << std::endl;

            // Check if timeout or an error occurred.
            if (ret <= 0)
            {
                std::cout << "return less than 0" << std::endl;
                std::cout << "closing socket descriptor" << std::endl;




                if (close(sd) < 0)
                {


                    char * newerrorMessage = strerror( errno); // get string message from errn
                    std::string newmsg (newerrorMessage);
                    std::cout << newmsg << std::endl;


                    std::cout << "failed to close socket descriptor" << std::cout;
                }



                return NULL;
            }
            else
            {
                // Check if select returned 1 due to an error.
                int valopt;
                socklen_t len = sizeof(int);

                getsockopt(sd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len);

                if (valopt)
                {
                    char * errorMessage = strerror( errno); // get string message from errn
                    std::string msg (errorMessage);
                    std::cout << msg << std::endl;

                    std::cout << "closing socket descriptor" << std::endl;

                    if (close(sd) < 0)
                    {

                        char * newerrorMessage = strerror( errno); // get string message from errn
                        std::string newmsg (newerrorMessage);
                        std::cout << newmsg << std::endl;


                        std::cout << "failed to close socket descriptor" << std::cout;
                    }


                    return NULL;

                }
            }

        }
        else
        {
            std::cout << "error but not EINPROGRESS" << std::endl;

            char * errorMessage = strerror( errno); // get string message from errn
            std::string msg (errorMessage);
            std::cout << msg << std::endl;






            if (close(sd) < 0)
            {


                char * newerrorMessage = strerror( errno); // get string message from errn
                std::string newmsg (newerrorMessage);
                std::cout << newmsg << std::endl;


                std::cout << "failed to close socket descriptor" << std::cout;
            }



            return NULL;
        }
    }

    // Return socket to blocking mode.
    arg = fcntl(sd, F_GETFL, NULL);
    arg &= (~O_NONBLOCK);
    fcntl(sd, F_SETFL, arg);

    // Create stream object.
    return new TCPStream(sd, &address);
}

TCPStream类:

/** @file    TCPStream.cpp
 *  @brief   cpp file to TCPStream class, which provides methods to send and receive 
 *           data over a TCP/IP connection.
 *  @author  Austin Small.
 */

#include "TCPStream.h"
#include <iostream>

/** @brief   TCPStream class constructor.
 *
 *  @param   argsd      Socket descriptor.
 *  @param   address    sockaddr_in struct.
 */

TCPStream::TCPStream(int argsd, struct sockaddr_in* address) :
    sd(argsd)
{
    char ip[50];

    // Convert a numeric address into a text string.
    //      struct sockaddr_in
    //      {
    //          short sin_family;
    //          unsigned short sin_port;
    //          struct in_addr sin_addr;
    //          char sin_zero[8];
    //      };
    //
    inet_ntop(PF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, sizeof(ip));

    peerIP = ip;

    // Convert from network byte order to host byte order.
    peerPort = ntohs(address->sin_port);
}

/** @brief   TCPComputerComm class destructor.
 *
 */

TCPStream::~TCPStream()
{
    std::cout << "closing fd" << std::endl;

    if (close(sd) < 0)
    {
        std::cout << "file descriptor not closed successfully" << std::endl;
    }
}

/** @brief   Wrapper function to send data.
 *
 *  @param   buffer     Pointer to first character of string.
 *  @param   len        Size of input string.
 *  @param   timeoutSec Timeout period for write command.
 *
 *  @return  Number of bytes written, -1 if a non-timeout error occured, and -2 if a timeout occurred.
 */

ssize_t TCPStream::send(const char* buffer, size_t len, int timeoutSec)
{
    // Attempt to send data with a timeout on write.
    fd_set set;
    FD_ZERO(&set);    // Clear the set.
    FD_SET(sd, &set); // Add our file descriptor to the set.

    struct timeval timeout;
    timeout.tv_sec  = timeoutSec;
    timeout.tv_usec = 0;

    int ret;
    ret = select(sd + 1, NULL, &set, NULL, &timeout);

    // First check if an error or timeout occurred.  Otherwise, call accept method.
    if (ret == -1)
    {
        return -1;
    }
    else if (ret == 0)
    {
        return -2;
    }
    else
    {
        return write(sd, buffer, len);
    }
}

/** @brief   Wrapper function to receive data.
 *
 *  @param   buffer     Pointer to first character of buffer to store received string.
 *  @param   len        Max number of bytes to read from file descriptor.
 *  @param   timeoutSec Timeout period for read command.
 *
 *  @return  Number of bytes read or -1 if a non-timeout error occurred and -2 if a timeout occurred.
 */

ssize_t TCPStream::receive(char* buffer, size_t len, int timeoutSec)
{
    // Attempt to send data with a timeout on write.
    fd_set set;
    FD_ZERO(&set);    // Clear the set.
    FD_SET(sd, &set); // Add our file descriptor to the set.

    struct timeval timeout;
    timeout.tv_sec  = timeoutSec;
    timeout.tv_usec = 0;

    int ret;
    ret = select(sd + 1, &set, NULL, NULL, &timeout);

    // First check if an error or timeout occurred.  Otherwise, call read method.
    if (ret == -1)
    {
        return -1;
    }
    else if (ret == 0)
    {
        return -2;
    }
    else
    {
        //std::cout << "attempting to read" << std::endl;
        return read(sd, buffer, len);
    }
}

/** @brief   Get peerIP address.
 *
 *  @return  peerIP address.
 */

string TCPStream::getPeerIP(void)
{
    return peerIP;
}

/** @brief   Get peer port.
 *
 *  @return  Peer port.
 */

int TCPStream::getPeerPort(void)
{
    return peerPort;
}

0 个答案:

没有答案