【问题标题】:Java -> C++ TCP Socket does not send until closeJava -> C++ TCP Socket 直到关闭才发送
【发布时间】:2012-05-26 10:12:29
【问题描述】:

我面临另一个 TCP 套接字问题。 我已经阅读了大量问题以及类似问题的答案,但我的问题在某种程度上有所不同。

我有一个 Java 客户端和 C++ 服务器。一切都按预期进行,直到我使用不同的机器(到目前为止等于其他问题) 来自客户端的消息似乎卡在了 den TCP 缓冲区中。当我最终关闭套接字时,所有内容都发送到服务器。但是这些单条消息是控制消息,所以我需要立即发送它们。据我所知,这是预期的行为,但我如何发送可靠的控制消息。

有没有办法强制发送消息。 (我可以让套接字打开几分钟而不发送任何内容。)

有什么问题吗? (见以下代码)

是否每次都必须关闭套接字才能执行真正的刷新?

我是否应该使用 UDP 来替代协议工作量?

Java代码:

mSocketSend = new Socket();
mSocketSend.connect(new InetSocketAddress(mServerIp, mSocketPortSend), mTimeOut);
PrintWriter pw = new PrintWriter(mSocketSend.getOutputStream(), true);
pw.println(data);

C++ 代码:

   opening socket...(i leave that)
   char* buffer = new char[1024];
   int rc = recv(mConnectedSocket, buf, 1024, 0);

如果你想要更多。写下来。我几乎把所有东西都留下了。 ^^ 我不认为它相关。沟通通常很顺利.. 完全没有错误。所以它只是这个 TCPBuffer 的东西。

我知道应该有一些分隔符或消息长度的东西。但实际上:没有发送的消息长度没有帮助。 ^^

感谢您的帮助。

EDIT #01 整个代码:

mSocket->createSocketServer(22);
    char* buffer = new char[1024];
 while(true){

        int numberBytes = mSocket->receiveChars(buffer, 1024);

        if (numberBytes > 0){
            uninterestingHandlingFunction(buffer);
        }else{
            mSocket->createSocketServer(22);
        }
    }

bool Socket::createSocketServer(u_short port)
{
    if (mConnectedSocket != INVALID_SOCKET)
    {
        closesocket(mConnectedSocket);
    }

    if (s == INVALID_SOCKET)
    {
        WSADATA wsa;

        if (WSAStartup(MAKEWORD(2,0), &wsa) != 0)
            return 0;

        s = socket(AF_INET, SOCK_STREAM, 0);
        if (s == INVALID_SOCKET)
            return 0;

        SOCKADDR_IN addr;
        memset(&addr, 0, sizeof(SOCKADDR_IN));
        addr.sin_family=AF_INET;
        addr.sin_port=htons(port);
        addr.sin_addr.s_addr=ADDR_ANY;

        if (bind(s, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
        {
            s = INVALID_SOCKET;
        } else if (listen(s, 10) == SOCKET_ERROR)
        {
            s = INVALID_SOCKET;
        }

        if (s == INVALID_SOCKET)
        {
            closesocket(s);
            return 0;
        }
    }
    mConnectedSocket = accept(s, NULL, NULL);

    if (mConnectedSocket == INVALID_SOCKET)
    {
        closesocket(s);
        return 0;
    }
    return 1;
}

int Socket::receiveChars(char* buf, unsigned maxSize)
{
    if (mConnectedSocket == INVALID_SOCKET)
        return -1;

    int rc = recv(mConnectedSocket, buf, maxSize, 0);
    if (rc == SOCKET_ERROR)
    {
        std::cout << "Socket: error " << WSAGetLastError() << std::endl;
    }
    return rc;
}

你想要它......

编辑 #2 再试一次

我尝试了更多的东西。 起初:这个问题不会每次都在通过真实网络连接的设备上发生。 -> 完全重启客户端和服务器 -> 问题没有发生 -> 完全重启客户端和服务器 -> 问题发生

可悲的是,我不知道如何从这个习惯中吸取教训。

我偶然发现的另一件事是绑定和侦听套接字(在 Code SOCKET 中)。此套接字侦听连接,如果工作线程需要一个新连接(在启动时或先前关闭时),套接字 s 将下一个排队的连接提供给 mConnectedSocket 以进行接收,其他连接在处理一个连接时会积压。 从 Java 视图:连接了一个 Socket(设备 A)。下一个套接字(设备 B)尝试连接。 -> 连接成功(如果确实发生,则在代码中正确控制)-> 随后以自然方式发送数据。 (socket在c++端还在积压中)

嗯,这很难转变为我所经历的习惯。我会尽量表达我的想法。 Javaside:创建 PrintWriter。输入数据并刷新。因为连接没有完全建立(C++端没有额外的connectedSocket)。冲洗不起作用。并且 onClose 套接字最终刷新了它的内容。

如果你这么认为,请告诉我闭嘴。我真的不知道“连接积压”在实施中的实际含义”^^

我知道,我应该为每个连接打开一个新线程,但我现在不能。所以坚持使用这个服务器代码。

【问题讨论】:

  • 你试过pw.flush()吗?
  • PrintWriter(OutputStream, boolean autoflush) 应该在 '\n' 上完成工作,但是是的.. 我也试过了
  • buf = 缓冲区?服务器等待 1024 字节或 eof?
  • 哦该死的。是的,一起复制的。缓冲区分配在其他地方,并作为 buf 传递给 receiveFunc。第二个问题是我真的不知道的事情。我目前的期望是当接收到至少一个字节时,recv 返回。
  • 是的,它也确实返回了 C++ 中的可用字节。

标签: java c++ sockets tcp stream


【解决方案1】:

修复它。有点尴尬... 我在编辑中注意到的积压确实是问题所在。 如果同时有两个 at 客户端连接到服务器,则第二个客户端会积压,当第一个客户端断开连接时,他的消息将被处理。

另外(线索来了)

如前所述,它是一个 android java 客户端。 java端还有另一个线程从C++服务器接收数据。此套接字连接到另一个端口。 但是我在设置活动中设置了要连接的端口和 IP 地址,并且另一个套接字的端口错误作为默认值(与问题套接字相同,采用了错误的变量) 所以这个socket首先连接,issuesocket连接到backlog中。 仅当我输入设置以设置另一个 IPAddress 时才采用此默认值(例如,当我连接到远程主机而不是本地主机时)

难以置信的情况...我什至没有写设置...

WIRESHARK 会解决这个问题。

【讨论】:

    【解决方案2】:

    您的循环编码不正确。每个新的 recv() 都会覆盖前一个。您应该提前偏移参数。

    【讨论】:

    • 你错了,处理函数在下一次recv调用之前取数据...无论如何...请坚持这个问题...当问题发生时recv没有返回一次...如果你指出每一个小细节,你不会帮助我。
    • 什么是“处理函数”?从什么时候开始试图以“小细节”回答这个问题?
    • recv 返回后调用 'uninterestingHandlingFunction(buffer)' 并获取缓冲区并处理他。 (并且根本不调用此函数-> recv 不返回)您给出的答案不能导致所描述的问题。我认为它是“您应该以这种方式做得更好”之一。-答案->不是解决方案。有点反应过激了,见谅。 ^^
    【解决方案3】:

    当您使用自动刷新,甚至尝试使用显式 flush()

    可能是因为您没有打开 inputStream。试着做一个getInputStream()

    否则,您是否尝试过:

    1. 您不使用connect 而是直接在Socket 构造函数中提供参数的任何差异?

    2. setTcpNoDelay在套接字上(但不应该造成几分钟的延迟!!)?

    【讨论】:

    • 嗯...好吧,getInputStream() 的事情很有趣。我为什么要那样做。我真的不明白为什么这会有所帮助。你能给我解释一下吗(我错误地在(工作)早期版本中使用了它) 1. 通信几乎每次都有效,所以我不认为这应该有所作为。 2. 我试过了,虽然我觉得不应该用这个。
    • 不应该真的用于普通的套接字/os/jvm/...,但对于像 HttpUrlConnections 这样的更高抽象来说,例如需要它(为 http 层创建响应/请求对象)所以很长,但我见过奇怪的事情发生在连接的另一端。真的应该为此使用wireshark。
    • 当我到达受影响的机器时,我会尝试这样做。这是与旧版本的唯一区别,旧版本运行良好。
    • 否定,getInputStream() 不会改变任何东西。
    【解决方案4】:

    实际上,您的问题可能出在接收端,您的recv 需要处于循环状态(您可以在 google 上搜索此示例)。不能保证每次拨打recv 都会得到多少。如果您知道您正在刷新 Java 站点上的数据,那可能是您的问题。

    【讨论】:

    • 它在一个循环中。我编辑了我的问题,让您更深入地了解代码。
    • 你的“uninterestingFunction”期望什么?通常,recv 循环将在执行 recv 的方法内部完成,以便在阻塞之前获取所有可用数据,而不是让您的“应用程序”函数处理部分数据。
    • 是的,你是对的。几周以来,我一直在试图说服我从这个问题中获得线索(分隔符/消息长度)。但他们说“不,这已经奏效了,所以它应该继续下去。”……但事实上他们是对的。我打印了立即发送的数据,但根本没有调用 uninterestingFunction。
    • “recv”的工作方式是非常特定于实现的,这就是为什么它可能在一个环境中工作而不是在另一个环境中工作的原因。你的 Java 东西可能没问题。
    • 为了让您更深入地了解:它实际上是一个 Android 应用程序。连接到 10.0.2.2(localhost) -> 工作正常。连接到 192.168.0.100(通过网络本地主机)-> 没有数据。所以是同一台机器。相同的构建。
    【解决方案5】:
    PrintWriter pw = new PrintWriter(mSocketSend.getOutputStream(), true);
    pw.println(data);
    pw.flush();
    

    【讨论】:

      【解决方案6】:
      PrintWriter.flush();
      

      或者使用具有自动刷新功能的写入器。

      您还应该确保服务器读取一行(直到 \n)而不是完整的 1024 个字符,但我不知道 recv() 做了什么,所以我不知道。

      【讨论】:

      • +1:recv() 将阻塞直到文件关闭或请求的数量被读取(除非其他选项导致它提前返回)。
      • 实际上recv()会阻塞直到它得到一些数据,但它可能不会读取所有数据。
      • 它不... recv() 返回,当有某事时。它可能会返回。一个字节就足够了。请记住:此程序有效,但并非每次都有效^^
      【解决方案7】:

      你需要做一个flush(),把数据推出来。

      【讨论】:

      • PrintWriter(OutputStream, boolean autoflush) 应该在 '\n' 上完成工作,但是是的.. 我也试过了。 mSocketSend.getOutputStream().flush() 也没有做这项工作。
      • 运行 wireshark 看看电线上发生了什么。
      • 是的,我也想过。没有安装 WinPCap 的管理员权限...所以我把工作带回家了,但神奇的是这里没有出现问题。
      • 查看我关于让你的 recv 循环播放的新答案。
      • 直接用getOutputStream试试?尝试在 C 端使用 select?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-05
      • 1970-01-01
      • 1970-01-01
      • 2013-07-21
      • 2017-09-04
      • 2014-10-05
      相关资源
      最近更新 更多