saofu5973 2016-12-15 11:09 采纳率: 0%
浏览 2339

C++ socket端口复用的问题

环境
公网服务器1台,内网服务器若干。其中一台内网可以和公网连接通信,打算在公网上用socket做端口转发来实现访各台问内网的目的。功能已经实现了(一对一连接),但端口复用问题一直很困惑,请各位帮忙检查一下代码,谢谢

公网代码

// 绑定两个服务器socket
void bind2bind(int port1, int port2)
{
    char msgbuf[100];
    memset(msgbuf, 0, 100);
    sprintf(msgbuf, "[+] 服务器初始化...\r\n");
    outprint(msgbuf, strlen(msgbuf));
    int fd0, fd1, fd2;
    if ((fd0 = create_socket()) == 0) return;
    if ((fd1 = create_socket()) == 0) return;
    if ((fd2 = create_socket()) == 0) return;

    int port0 = 5501;
    if (create_server(fd0, port0) == 0)
    {
        closesocket(fd0);
        return;
    }
    sprintf(msgbuf, "[+] 成功监听通信端口: %d\r\n", port0);
    outprint(msgbuf, strlen(msgbuf));

    // 创建第一个服务器
    if (create_server(fd1, port1) == 0)
    {
        closesocket(fd1);
        return;
    }
    sprintf(msgbuf, "[+] 成功监听隧道端口: %d\r\n", port1);
    outprint(msgbuf, strlen(msgbuf));

    // 创建第二个服务器
    if (create_server(fd2, port2) == 0)
    {
        closesocket(fd2);
        return;
    }
    sprintf(msgbuf, "[+] 成功监听上线端口: %d\r\n", port2);
    outprint(msgbuf, strlen(msgbuf));

    // 等待两端上线
    while (1)
    {
        sprintf(msgbuf, "[%d] 正在建立通信端口: %d\r\n", idx, port0);
        outprint(msgbuf, strlen(msgbuf));
        struct sockaddr_in client0;
        int size0 = sizeof(struct sockaddr);
        int sockfd0 = accept(fd0, (struct sockaddr *)&client0, &size0);
        sprintf(msgbuf, "[%d] 通信端口连接成功 IP地址: %s\r\n", idx, inet_ntoa(client0.sin_addr));
        outprint(msgbuf, strlen(msgbuf));

        sprintf(msgbuf, "[%d] 发送idx值: %d\r\n", idx, idx);
        outprint(msgbuf, strlen(msgbuf));
        char idxbuf[10];
        memset(idxbuf, 0, 10);
        sprintf(idxbuf, "%d", idx);
        int send0 = send(sockfd0, idxbuf, strlen(idxbuf)+1, 0);
        if (send0 <= 0)
        {
            closesocket(sockfd0);
            continue;
        }
        closesocket(sockfd0);

        sprintf(msgbuf, "[%d] 正在建立隧道端口: %d\r\n", idx, port1);
        outprint(msgbuf, strlen(msgbuf));
        struct sockaddr_in client1;
        int size1 = sizeof(struct sockaddr);
        int sockfd1 = accept(fd1, (struct sockaddr *)&client1, &size1);

        sprintf(msgbuf, "[%d] 正在建立上线端口: %d\r\n", idx, port2);
        outprint(msgbuf, strlen(msgbuf));
        struct sockaddr_in client2;
        int size2 = sizeof(struct sockaddr);
        int sockfd2 = accept(fd2, (struct sockaddr *)&client2, &size2);
        sprintf(msgbuf, "[%d] 通过上线端口连接成功 IP地址: %s\r\n", idx, inet_ntoa(client2.sin_addr));
        outprint(msgbuf, strlen(msgbuf));

        // socket
        transocket sock;
        sock.fd1 = sockfd1;
        sock.fd2 = sockfd2;
        sock.idx = idx;
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)transmitdata, (LPVOID)&sock, 0, NULL);
        idx++;
    }
}
负责通信的内网代码
{
        int port0 = 5501;
        char msgbuf[100];
        memset(msgbuf, 0, 100);
        while (1)
        {
            int sockfd0;
            if ((sockfd0 = create_socket()) == 0) return 0;

            // 通过5501端口连接服务器
            sprintf(msgbuf, "[+] 正在连接服务器 %s:%d\r\n", sConnectHost, port0);
            outprint(msgbuf, strlen(msgbuf));
            if (client_connect(sockfd0, sConnectHost, port0) == 0)
            {
                closesocket(sockfd0);
                continue;
            }
            sprintf(msgbuf, "[+] 服务器连接成功\r\n");
            outprint(msgbuf, strlen(msgbuf));

            // 接收服务器idx值
            char idxbuf[10];
            memset(idxbuf, 0, 10);
            int read0 = recv(sockfd0, idxbuf, 10, 0);
            if (read0 > 0)
            {
                // 在线程内执行conn2conn
                int nIdx = atoi(idxbuf);
                tranconn2conn conn;
                conn.host1 = sConnectHost;
                conn.port1 = iConnectPort;
                conn.host2 = sTransmitHost;
                conn.port2 = iTransmitPort;
                conn.idx = nIdx;
                CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)conn2conn, (LPVOID)&conn, 0, NULL);
            }
        }
        closeallfd();
}

// 内网转发数据
void conn2conn(LPVOID data)
{
    tranconn2conn *conn = (tranconn2conn *)data;

    char msgbuf[100];
    memset(msgbuf, 0, 100);
    while (1)
    {
        int sockfd1, sockfd2;
        if ((sockfd1 = create_socket()) == 0) return;
        if ((sockfd2 = create_socket()) == 0) return;

        sprintf(msgbuf, "[%d] 正在连接服务器 %s:%d\r\n", conn->idx, conn->host1, conn->port1);
        outprint(msgbuf, strlen(msgbuf));
        if (client_connect(sockfd1, conn->host1, conn->port1) == 0)
        {
            closesocket(sockfd1);
            closesocket(sockfd2);
            continue;
        }
        sprintf(msgbuf, "[%d] 服务器连接成功\r\n", conn->idx);
        outprint(msgbuf, strlen(msgbuf));

        sprintf(msgbuf, "[%d] 正在连接内网 %s:%d\r\n", conn->idx, conn->host2, conn->port2);
        outprint(msgbuf, strlen(msgbuf));
        if (client_connect(sockfd2, conn->host2, conn->port2) == 0)
        {
            closesocket(sockfd1);
            closesocket(sockfd2);
            continue;
        }
        sprintf(msgbuf, "[%d] 内网连接成功\r\n", conn->idx);
        outprint(msgbuf, strlen(msgbuf));

        // socket
        transocket sock;
        sock.fd1 = sockfd1;
        sock.fd2 = sockfd2;
        sock.idx = conn->idx;
        transmitdata(&sock);
    }
}
数据转发代码
// 服务器转发数据
void transmitdata(LPVOID data)
{
    transocket *sock = (transocket *)data;

    // 准备采用select模型
    fd_set readfd, writefd;
    char read_in1[MAXSIZE], send_out1[MAXSIZE];
    char read_in2[MAXSIZE], send_out2[MAXSIZE];

    struct sockaddr_in client1, client2;
    char host1[20], host2[20], msgbuf[100];
    memset(host1, 0, 20);
    memset(host2, 0, 20);
    memset(msgbuf, 0, 100);

    int port1 = 0, port2 = 0;
    int structsize1 = sizeof(struct sockaddr);
    int structsize2 = sizeof(struct sockaddr);

    // 获取第一客户端信息
    if (getpeername(sock->fd1, (struct sockaddr *)&client1, &structsize1) < 0)
    {
        strcpy(host1, "fd1");
    }
    else
    {
        strcpy(host1, inet_ntoa(client1.sin_addr));
        port1 = ntohs(client1.sin_port);
    }

    // 获取第二客户端信息
    if (getpeername(sock->fd2, (struct sockaddr *)&client2, &structsize2) < 0)
    {
        strcpy(host2, "fd2");
    }
    else
    {
        strcpy(host2, inet_ntoa(client2.sin_addr));
        port2 = ntohs(client2.sin_port);
    }

    sprintf(msgbuf, "[%d] fd1: %d, fd2: %d\r\n", sock->idx, sock->fd1, sock->fd2);
    outprint(msgbuf, strlen(msgbuf));

    // 输出两个客户端信息
    sprintf(msgbuf, "[%d] 建立数据转发走向(%s:%d <-> %s:%d)\r\n", sock->idx, host1, port1, host2, port2);
    outprint(msgbuf, strlen(msgbuf));

    // 最大检测数目
    int maxfd = max(sock->fd1, sock->fd2) + 1;

    // 设置检测超时
    struct timeval timeset;
    timeset.tv_sec = TIMEOUT;           // 超时秒数
    timeset.tv_usec = 0;                // 超时微秒

    // 位模式清零
    memset(send_out1, 0, MAXSIZE);
    memset(send_out2, 0, MAXSIZE);

    // 可读长度
    int totalread1 = 0, totalread2 = 0;
    while (1)
    {
        // 采用select异步模型
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);

        // 添加到fd集合
        FD_SET(sock->fd1, &readfd);
        FD_SET(sock->fd1, &writefd);
        FD_SET(sock->fd2, &readfd);
        FD_SET(sock->fd2, &writefd);

        // 第一个参数会被系统忽略掉
        int result = select(maxfd, &readfd, &writefd, NULL, &timeset);
        if ((result < 0) && (errno != EINTR))
        {
            // 程序出错
            sprintf(msgbuf, "[%d] Error: select函数出错\r\n", sock->idx);
            outprint(msgbuf, strlen(msgbuf));
            break;
        }
        else if (result == 0)
        {
            // 等待超时
            sprintf(msgbuf, "[%d] Error: select函数等待超时\r\n", sock->idx);
            outprint(msgbuf, strlen(msgbuf));
            continue;
        }

        // fd1可读, 只要可读, 一次即可读取完整, 无需while循环
        if (FD_ISSET(sock->fd1, &readfd) && totalread1 < MAXSIZE)
        {
            int read1 = recv(sock->fd1, read_in1, MAXSIZE - totalread1, 0);
            if (read1 == 0 || (read1 < 0 && errno != EINTR))
            {
                sprintf(msgbuf, "[%d] Error: 读取fd1出错\r\n", sock->idx);
                outprint(msgbuf, strlen(msgbuf));
                break;
            }

            // 改变fd1的发送缓冲区和他的大小(接了就要发嘛)
            memcpy(send_out1 + totalread1, read_in1, read1);
            sprintf(msgbuf, "[%d] fd1成功在地址: %16s:%d上面读取%d字节\r\n", sock->idx, host1, port1, read1);
            outprint(msgbuf, strlen(msgbuf));
            totalread1 += read1;
            memset(read_in1, 0, MAXSIZE);
        }

        // fd2可写
        if (FD_ISSET(sock->fd2, &writefd))
        {
            int err = 0;
            int sendcount1 = 0;

            // 发送的话可能一次发不完(数据包过大)
            while (totalread1 > 0)
            {
                int send1 = send(sock->fd2, send_out1+sendcount1, totalread1, 0);
                if (send1 == 0 || (send1 < 0 && errno != EINTR))
                {
                    sprintf(msgbuf, "[%d] Error: 写入fd2出错\r\n", sock->idx);
                    outprint(msgbuf, strlen(msgbuf));
                    err = 1;
                    break;
                }
                if ((send1 < 0) && (errno == ENOSPC)) break;

                // 改变缓冲区大小
                sendcount1 += send1;
                totalread1 -= send1;
                sprintf(msgbuf, "[%d] fd2成功在地址: %16s:%d上面发送%d字节\r\n", sock->idx, host2, port2, send1);
                outprint(msgbuf, strlen(msgbuf));
            }

            // 检验出错
            if (err == 1) break;

            // 如果发送了数据, 并且没有发送干净
            if ((totalread1 > 0) && (sendcount1 > 0))
            {
                // 理论上下面两行永远不会执行
                memcpy(send_out1, send_out1 + sendcount1, totalread1);
                memset(send_out1 + totalread1, 0, MAXSIZE - totalread1);
            }
            else
            {
                memset(send_out1, 0, MAXSIZE);
            }
        }

        // fd2可读, 只要可读, 一次即可读取完整, 无需while循环
        if (FD_ISSET(sock->fd2, &readfd) && totalread2 < MAXSIZE)
        {

            int read2 = recv(sock->fd2, read_in2, MAXSIZE - totalread2, 0);
            if (read2 == 0 || (read2 < 0 && errno != EINTR))
            {
                sprintf(msgbuf, "[%d] Error: 读取fd2出错\r\n", sock->idx);
                outprint(msgbuf, strlen(msgbuf));
                break;
            }

            // 接了就立即发出去
            memcpy(send_out2 + totalread2, read_in2, read2);
            sprintf(msgbuf, "[%d] fd2成功在地址: %16s:%d上面读取%d字节\r\n", sock->idx, host2, port2, read2);
            outprint(msgbuf, strlen(msgbuf));
            totalread2 += read2;
            memset(read_in2, 0, MAXSIZE);
        }

        // fd1可写
        if (FD_ISSET(sock->fd1, &writefd))
        {
            int err2 = 0;
            int sendcount2 = 0;
            while (totalread2 > 0)
            {
                int send2 = send(sock->fd1, send_out2 + sendcount2, totalread2, 0);
                if (send2 == 0 || (send2 < 0 && errno != EINTR))
                {
                    sprintf(msgbuf, "[%d] Error: 写入fd1出错\r\n", sock->idx);
                    outprint(msgbuf, strlen(msgbuf));
                    err2 = 1;
                    break;
                }
                if ((send2 < 0) && (errno == ENOSPC)) break;
                sendcount2 += send2;
                totalread2 -= send2;
                sprintf(msgbuf, "[%d] fd1成功在地址: %16s:%d上面发送%d字节\r\n", sock->idx, host1, port1, send2);
                outprint(msgbuf, strlen(msgbuf));
            }
            if (err2 == 1) break;
            if ((totalread2 > 0) && (sendcount2 > 0))
            {
                memcpy(send_out2, send_out2+sendcount2, totalread2);
                memset(send_out2 + totalread2, 0, MAXSIZE - totalread2);
            }
            else
            {
                memset(send_out2, 0, MAXSIZE);
            }
        }
    }

    // 关闭socket
    closesocket(sock->fd1);
    closesocket(sock->fd2);
    sprintf(msgbuf, "[%d] 成功关闭掉两个socket, fd1, fd2\r\n", sock->idx);
    outprint(msgbuf, strlen(msgbuf));
    sprintf(msgbuf, "[%d] 本次数据转发结束\r\n\n", sock->idx);
    outprint(msgbuf, strlen(msgbuf));
}

  • 写回答

1条回答 默认 最新

  • dabocaiqq 2016-12-17 04:11
    关注
    评论

报告相同问题?

悬赏问题

  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 lammps拉伸应力应变曲线分析
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥15 请问Lammps做复合材料拉伸模拟,应力应变曲线问题
  • ¥30 python代码,帮调试,帮帮忙吧
  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建