【问题标题】:Java client C++ server data send/receive problemJava客户端C++服务器数据发送/接收问题
【发布时间】:2019-02-25 19:52:51
【问题描述】:

我创建了一个简单的 java 客户端和一个 c++ 服务器。我正在尝试将一些数据从客户端传输到服务器。问题是,当服务器接收到数据时,它会显示一些错误符号,并且当服务器向 java 客户端返回一些响应时,我再次看到那些错误符号。你能帮我解决这个问题吗..

java 客户端

 public Client(String host, int port) {
        try {
            String serverHostname = new String(host);

            System.out.println("Connecting to host " + serverHostname + " on port " + port + ".");

            Socket echoSocket = null;
            PrintWriter out = null;
            BufferedReader in = null;

            try {
                echoSocket = new Socket(serverHostname, port);
                out = new PrintWriter(echoSocket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
            } catch (UnknownHostException e) {
                System.err.println("Unknown host: " + serverHostname);
                System.exit(1);
            } catch (IOException e) {
                System.err.println("Unable to get streams from server");
                System.exit(1);
            }

            /** {@link UnknownHost} object used to read from console */
            BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));

            while (true) {
                System.out.print("client: ");

                String userInput = stdIn.readLine();
                /** Exit on 'q' char sent */
                if ("q".equals(userInput)) {
                    break;
                }
                out.println(userInput);
                System.out.println("server: " + in.readLine());
            }

            /** Closing all the resources */
            out.close();
            in.close();
            stdIn.close();
            echoSocket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

c++ 服务器

int main()
{
    SOCKET ConSock;
    SOCKET ListenSock;
    SOCKADDR_IN address;
    int addsize = sizeof(address);

    long ok;
    char MESSAGE[200];

    WSAData WSD;
    WORD DllVersion = MAKEWORD(2, 1);

    ok = WSAStartup(DllVersion, &WSD);

    ConSock = socket(AF_INET,SOCK_STREAM,NULL);
    address.sin_addr.s_addr = inet_addr("10.64.15.3");
    address.sin_family = AF_INET;
    address.sin_port = htons(10102);

    ListenSock = socket(AF_INET,SOCK_STREAM,NULL);

    bind(ListenSock,(SOCKADDR*)&address,sizeof(address));
    listen(ListenSock,SOMAXCONN);

    cout<<"Waiting for connection\n";

    while(1)
    {
        if(ConSock = accept(ListenSock,(SOCKADDR*)&address,&addsize))
        {

            ok = recv(ConSock,MESSAGE,sizeof(MESSAGE),NULL);

            string msg;
            msg = MESSAGE;

            cout<<"Client says:\t"<<msg;
            string reply;
            cout<<"\nEnter reply:";
            cin>>reply;

            const char* rep = reply.c_str();

            ok = send(ConSock,rep,1024,NULL);

        }

    }
}

这是我从客户端获得的输入图片: c++ server image

【问题讨论】:

  • “坏符号”听起来像是字符编码问题。您使用的是什么编码(两端)。
  • 问题是你没有在任何地方指定长度并且没有空终止你发送的字符串
  • @PeterT 我已经在 c++ 服务器中指定了长度
  • @K.Krunk 这里没有指定长度msg = MESSAGE;
  • if(ok&gt;0){ msg.assign(MESSAGE, ok); } 似乎更合适,将ok 重命名为bytesRead 可能有助于清晰

标签: java c++ sockets server


【解决方案1】:

Java 字符是两个字节。如果您在输出/输入时对其进行编码/解码,它可能会起作用(假设您也正确传达了字符串长度,我还没有检查过)。

StandardCharsets.ISO_8859_1.encode(chars).array() -> byte array
StandardCharsets.ISO_8859_1.decode(bytes).array() -> char array

当你开始发送文本以外的内容或选择发送 unicode 并在 C++ 端使用 wstring 时,接下来会中断的事情——Java 使用网络字节顺序,C++——主机字节顺序(与 Java 上的相反) x86)。这是原因之一。

最后一条建议 - 先让它在 Java-Java 或 C++-C++ 中运行(无论你说哪种语言更好),然后切换到调试混合 Java-C++。

【讨论】:

  • 实际上我的目的是通过 XML 进行通信,但是我如何编码从客户端发送的字符串?
  • Java 方式是直接使用Netty + XStreamJAXB/CXF,而不是自己编写网络代码。
【解决方案2】:

您只是不会在 C++ 端的任何地方检查缓冲区。

在这部分

        ok = recv(ConSock,MESSAGE,sizeof(MESSAGE),NULL);

        string msg;
        msg = MESSAGE;

您只需将所有字节分配给msg,直到它看到 0,但您从未终止它,所以它只是打印内存中的随机内容。顺便说一下,这也是潜在的缓冲区溢出。

更合适的可能是

        auto byte_count = recv(ConSock,MESSAGE,sizeof(MESSAGE),NULL);

        string msg;
        if(byte_cout > 0 )
        {
            msg.assign(MESSAGE, byte_count);
        }

当您在此处发送整个缓冲区时会执行类似的操作:

        cin>>reply;
        const char* rep = reply.c_str();
        ok = send(ConSock,rep,1024,NULL);

无论你的缓冲区有多大,总是发送 1024 字节也可能导致缓冲区溢出。

这样的东西可能会更好

        cin>>reply;
        ok = send(ConSock,reply.c_str(),reply.size(),NULL);

编辑:具有更明确定义的“消息结束”状态的完整程序会有点像这样

Java

public Client(String host, int port) {
    try {
        String serverHostname = new String(host);

        System.out.println("Connecting to host " + serverHostname + " on port " + port + ".");

        Socket echoSocket = null;
        PrintWriter out = null;
        BufferedReader in = null;

        try {
            echoSocket = new Socket(serverHostname, port);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Unknown host: " + serverHostname);
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Unable to get streams from server");
            System.exit(1);
        }

        /** {@link UnknownHost} object used to read from console */
        BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));

        while (true) {
            System.out.print("client: ");

            String userInput = stdIn.readLine();
            /** Exit on 'q' char sent */
            if ("q".equals(userInput)) {
                break;
            }
            out.println(userInput);
            //send a newline as a marker for "message is over"
            out.write('\n');
            System.out.println("server: " + in.readLine());
        }

        /** Closing all the resources */
        out.close();
        in.close();
        stdIn.close();
        echoSocket.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

C++端

int main()
{
    SOCKET ConSock;
    SOCKET ListenSock;
    SOCKADDR_IN address;
    int addsize = sizeof(address);

    long ok;
    char MESSAGE[200];

    WSAData WSD;
    WORD DllVersion = MAKEWORD(2, 1);

    ok = WSAStartup(DllVersion, &WSD);

    ConSock = socket(AF_INET,SOCK_STREAM,NULL);
    address.sin_addr.s_addr = inet_addr("10.64.15.3");
    address.sin_family = AF_INET;
    address.sin_port = htons(10102);

    ListenSock = socket(AF_INET,SOCK_STREAM,NULL);

    bind(ListenSock,(SOCKADDR*)&address,sizeof(address));
    listen(ListenSock,SOMAXCONN);

    cout<<"Waiting for connection\n";

    while(1)
    {
        if(ConSock = accept(ListenSock,(SOCKADDR*)&address,&addsize))
        {
            string msg;
            bool message_finished = false;
            //read into msg until we read a newline at the end or we get an error
            while(!message_finished)
            {
                auto length_read = recv(ConSock,MESSAGE,sizeof(MESSAGE),NULL);
                if(length_read > 0)
                {
                    msg.append(MESSAGE,length_read);
                    message_finished = msg[msg.size()-1] == '\n';
                }
                else if(length_read < 0)
                {
                   //don't continue to just endlessly loop on error
                   message_finished = true;
                }
            }

            cout<<"Client says:\t"<<msg;
            string reply;
            cout<<"\nEnter reply:";
            cin>>reply;
            //append a newline at the end to mark the end of the message
            reply+='\n';
            ok = send(ConSock, reply.c_str(), reply.size(),NULL);

        }

    }
}

【讨论】:

  • 很好,但是现在当我响应客户端(java 应用程序)时,服务器停止,并且没有响应:/
  • 您确定问题出在服务器端?因为在我看来,您的 java 程序似乎正在等待它得到一个带有in.readLine() 的换行符,但您从未在服务器中发送过一个换行符。也许澄清实际协议应该如何工作,因为目前在 C++ 端读取多少实际传入消息是完全任意的,您可以通过许多其他方式轻松将其死锁,例如从 java 端发送超过 200 个字符。
  • 问题是我对socket编程很陌生,如何增加输入到服务器的字符串大小,这样就不会出现死锁,我应该如何更改java应用程序正确接收服务器响应。
  • 我添加了一些最小修改的示例,我没有尝试编译或验证,它仍然存在一些问题,例如忽略发送时的错误(这可能导致无法发送大消息回复)。但我认为这已经超出了最初的问题所要求的范围,即“我如何摆脱这些不好的符号?”如果不了解实际问题是什么,代码对您没有多大帮助。
  • 您还应该注意,您在两侧都在两个不同的抽象级别上进行操作。在 C++ 端,您使用原始套接字,在 Java 端,您使用缓冲输入/输出流库。也许像 asio 这样的网络库在 C++ 端更合适。它只是不是 C++ 标准库的一部分,所以如果你不想手动编码缓冲等所有次要细节,可以考虑一下。
猜你喜欢
  • 2014-04-08
  • 2017-08-20
  • 1970-01-01
  • 2013-10-02
  • 2017-12-29
  • 2021-12-04
  • 2015-12-26
  • 2021-08-24
  • 2016-02-23
相关资源
最近更新 更多