【问题标题】:C++ Socket Receiving Erroneous Commands, not intended onesC++ 套接字接收错误命令,不是预期的
【发布时间】:2012-03-28 22:50:28
【问题描述】:

我正在学习用于即将进行的项目的套接字编程,并且我对这个问题进行了相当广泛的研究。基本上,这个程序需要在客户端计算机(本地,即我的计算机)上连接到远程服务器并发送命令(它已经完成,我已经让它读回 Apache 服务器统计信息给我)。

发生的事情是这样的:我相信我的套接字设置正确,但服务器接收到随机垃圾缓冲区(其中一个由“'>Z”组成)。我尝试了各种套接字设置、不同的绑定等。

我在启动的过程中会初始化winsock,创建socket,绑定网络,然后做一个listen loop和while(1)recv数据。

我还没有让服务器(在远程计算机上,托管在数据中心)来输出消息。这是我目前唯一的目标。提前感谢大家的帮助,代码是之前的(这是完整的代码,篇幅见谅)。

客户代码:

char *host = "127.0.0.1";
        SOCKET clientsock;
        struct sockaddr_in server_address;

        struct hostent *host_info;

        WSADATA WSAData;
        if(WSAStartup(MAKEWORD(2,2), &WSAData) != -1) {
            cout << "WINSOCK2 Initialized" << endl;         
            if((clientsock = socket(AF_INET, SOCK_STREAM, 0)) != SOCKET_ERROR) {
                cout << "Socket Created" << endl;

                char opt[2];
                opt[0] = 0;
                opt[1] = 1;

                //setsockopt(clientsock, SOL_SOCKET, SO_BROADCAST, opt, sizeof(opt));

                host_info = gethostbyname(host);                    

                server_address.sin_family = AF_INET;
                server_address.sin_addr = *((struct in_addr *)host_info->h_addr);                   
                server_address.sin_port = htons(80);

                if(connect(clientsock, (struct sockaddr *)&server_address, sizeof(struct sockaddr)) == 0) {
                    cout << "Connected to host" << endl;
                    char COMMAND[22] = "SVR --WINSOCK-VERIFY\0";
                    if(send(clientsock, COMMAND, sizeof(COMMAND), 0)) {
                        cout << "Command Sent" << endl;                         
                        closesocket(clientsock);
                    }
                    else {
                        cout << "ERROR - Could not send command. " << "Error: " << WSAGetLastError() << endl;
                        closesocket(clientsock);
                        WSACleanup();
                    }
                }
                else {
                    cout << "ERROR - Could not connect to host. " << "Error: " << WSAGetLastError() << endl;
                    closesocket(clientsock);
                    WSACleanup();
                }                   
            }
            else {
                cout << "ERROR - Could not create the socket. " << "Error: " << WSAGetLastError() << endl;                  
                WSACleanup();
            }               
        }
        else {
            cout << "ERROR - Could not initialize WINSOCK2. " << "Error: " << WSAGetLastError() << endl;                                
            WSACleanup();
        }

服务器代码:

SOCKET serversock;
        char *server = "127.0.0.1";     
        //char *server = "50.31.1.180"; 
        struct sockaddr_in server_address;

        WSADATA WSAData;
        if(WSAStartup(MAKEWORD(2,2), &WSAData) != -1) {
            cout << "WINSOCK2 Initialized" << endl;

            if((serversock = socket(PF_INET, SOCK_DGRAM, PF_UNSPEC)) != SOCKET_ERROR) {
                cout << "Socket Created" << endl;

                unsigned long NB = 1;
                ioctlsocket(serversock, FIONBIO, &NB);

                server_address.sin_family = AF_INET;
                server_address.sin_addr = *((struct in_addr *)server);
                server_address.sin_port = htons(21578); 

                if(bind(serversock, (struct sockaddr*)&server_address, sizeof(struct sockaddr) == 0)) {
                    cout << "Network bound" << endl;                    

                    cout << "Listening..." << endl;
                    listen(serversock, 5);                      
                    while(1) {                  
                        int size = sizeof((struct sockaddr *)server);
                        SOCKET clientsock = accept(serversock, (struct sockaddr *)server, &size);
                        char INCOMMAND[20];
                        if(clientsock >= 0) { 
                            if(recv(clientsock, INCOMMAND, sizeof(INCOMMAND), 0)) {                             
                                int i = 0;  
                                if(INCOMMAND == "SVR --WINSOCK-VERIFY\0") {
                                    cout << "SVR receieved" << endl;
                                }
                                while(INCOMMAND[i] != '\0') {
                                    cout << INCOMMAND[i];                                       
                                    i++;                                                                            
                                }                                   
                                cout << endl;                                   
                            }
                            else {
                                cout << "ERROR - Could not receive command" << endl;
                                break;
                            }
                        }
                    }                       
                }
                else {
                    cout << "ERROR - Could not bind network. " << "Error: " << WSAGetLastError() << endl;
                    closesocket(serversock);
                    WSACleanup();
                }
            }
            else {
                cout << "ERROR - Could not create the socket. " << "Error: " << WSAGetLastError() << endl;                  
                WSACleanup();
            }
        }
        else {
            cout << "ERROR - Could not initialize WINSOCK2. " << "Error: " << WSAGetLastError() << endl;                                
            WSACleanup();
        }

【问题讨论】:

    标签: c++ sockets winsock


    【解决方案1】:

    send/recv 的调用可能不会发送/接收您在第三个参数中指定的字节数,事实上,大多数时候它们发送/接收的字节数比您预期的要少。您通常必须循环直到发送/接收整个数据。另请注意,这样做:

    char buffer[100];
    recv(clientsock, buffer, sizeof(buffer), 0);
    cout << buffer;
    

    肯定会打印垃圾,因为您的 char 数组中没有空终止符(附加它时会出现缓冲区溢出),并且您没有检查 recv 的返回值。它可能只读取 1 个字节(如果发生错误,则不读取)。您在服务器应用程序中以相同的方式打印缓冲区。

    在这种情况下,您实际上是在发送空终止符,但是由于您读取的字节数可能比您预期的要少,因此其他应用程序可能不会接收到该字符,因此打印它会打印垃圾字符。

    编辑:您应该看看sockaddr 结构的结构。你可以看看here。在您的代码中,您正在使用此转换:

    int size = sizeof((struct sockaddr *)"127.0.0.1");
    

    const char * 是“127.0.0.1”的类型,不能转换为 sockaddr 指针,它们不兼容。这里你应该使用getaddrinfo来解析IP地址(注意你可以使用域名,这个函数会解析它)。网上有很多关于如何使用这个功能的教程,只需搜索“getaddrinfo”即可。

    【讨论】:

    • 谢谢你。我在字符串中包含了一个空字符 (\0),并使用循环在服务器端对其进行排序。该程序仍然认为它正在接收命令,尽管它只是垃圾数据。我现在的问题是:使用更正的代码(我最初在帖子中编辑过),现在在 localhost 上运行以进行测试,将不会传递命令。客户端读回正确连接并发送命令,但服务器没有显示任何迹象。有什么想法吗?
    • 从“手册”页上看并不完全清楚,但实际上阻止 send() 将发送整个消息,如有必要会阻止,直到它可以这样做为止。
    • @EJP 手册页确实提到了这一点。但是,MSDN 声明“如果没有发生错误,则 send 返回发送的总字节数,可以小于 len 参数中请求发送的数量。”。因此,在发送时循环是正确的,除非您希望您的代码依赖于平台。顺便说一句,OP 使用的是 Windows,因此发送时循环是正确的。
    • Fontanini,我曾经在我的原始帖子中编辑过代码。我已经更改了转换为结构的字符格式以匹配其请求的类型,即 char。现在,代码仍然可以工作,我对如何准确地确定如何使用 getaddrinfo() 函数几乎没有迷失。我对 C++ 还有些陌生,很抱歉,感谢您的帮助。此外,我没有从服务器监视器窗口收到非垃圾重复值,例如 PBR。
    • 您仍在使用错误的 sockaddr 结构。我建议您阅读本教程:beej.us/guide/bgnet/output/html/singlepage/bgnet.html。这是一个很棒的教程,它解释了如何处理 sockaddr 结构、连接、绑定以及使它工作所需的一切。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    • 2011-07-31
    • 1970-01-01
    相关资源
    最近更新 更多