Winsock-从C++; 我有一个客户机-服务器应用程序,服务器部分用C++编写(Winsock),客户端部分用java编写。

Winsock-从C++; 我有一个客户机-服务器应用程序,服务器部分用C++编写(Winsock),客户端部分用java编写。,c++,windows,sockets,types,winsock,C++,Windows,Sockets,Types,Winsock,当从客户端发送数据时,我首先发送它的长度,然后是实际数据。发送长度的代码如下: clientSender.print(text.length()); 其中clientSender的类型为PrintWriter 在服务器端,读取此信息的代码是 int iDataLength; if(recv(client, (char *)&iDataLength, sizeof(iDataLength), 0) != SOCKET_ERROR) //do something 我尝试在if中打

当从客户端发送数据时,我首先发送它的长度,然后是实际数据。发送长度的代码如下:

clientSender.print(text.length());
其中
clientSender
的类型为
PrintWriter

在服务器端,读取此信息的代码是

int iDataLength;
if(recv(client, (char *)&iDataLength, sizeof(iDataLength), 0) != SOCKET_ERROR)
    //do something
我尝试在
if
中打印
iDataLength
的值,结果总是某个随机大整数。如果我将
iDataLength
的类型更改为
char
,则会得到正确的值。但是,实际值可能远远超过
char
的容量


< C++中正确读取一个整数的方法是什么?

你确定循环数不是问题吗?(可能Java将其编码为big-endian,而您将其读取为little-endian)


此外,您可能需要实现
receival
功能(类似于
sendall
-as)。确保您收到指定的确切字节数-因为
recv
接收的字节数可能比它被告知的要少。

您确定endianness不是问题所在吗?(可能Java将其编码为big-endian,而您将其读取为little-endian)


此外,您可能需要实现
receival
功能(类似于
sendall
-as)。确保您收到指定的确切字节数-因为
recv
收到的字节数可能比它被告知的要少。

我认为问题在于PrintWriter正在写入文本,而您正在尝试读取二进制数

以下是PrintWriter对其发送的整数所做的操作:

打印一个整数。string.valueOf(int)生成的字符串为 根据平台的默认字符转换为字节 编码,这些字节完全按照 write(int)方法

试着这样做:

#include <sys/socket.h>
#include <cstring> // for std::strerror()

// ... stuff

char buf[1024]; // buffer to receive text
int len;

if((len = recv(client, buf, sizeof(buf), 0)) == -1)
{
    std::cerr << "ERROR: " << std::strerror(errno) << std::endl;
    return 1;
}

std::string s(buf, len);

int iDataLength = std::stoi(s); // convert text back to integer

// use iDataLength here (after sanity checks)
#包括
#包含//用于std::strerror()
// ... 东西
字符buf[1024];//接收文本的缓冲区
内伦;
如果((len=recv(客户机,buf,sizeof(buf),0))=-1)
{

std::cerr我认为问题在于PrintWriter正在写文本,而您正试图读取一个二进制数

以下是PrintWriter对其发送的整数所做的操作:

打印一个整数。string.valueOf(int)生成的字符串为 根据平台的默认字符转换为字节 编码,这些字节完全按照 write(int)方法

试着这样做:

#include <sys/socket.h>
#include <cstring> // for std::strerror()

// ... stuff

char buf[1024]; // buffer to receive text
int len;

if((len = recv(client, buf, sizeof(buf), 0)) == -1)
{
    std::cerr << "ERROR: " << std::strerror(errno) << std::endl;
    return 1;
}

std::string s(buf, len);

int iDataLength = std::stoi(s); // convert text back to integer

// use iDataLength here (after sanity checks)
#包括
#包含//用于std::strerror()
//…东西
char buf[1024];//用于接收文本的缓冲区
内伦;
如果((len=recv(客户机,buf,sizeof(buf),0))=-1)
{

std::cerr数值和ASCII表示之间存在混淆

在Java中编写
clientSender.print(text.length());
实际上是在编写ascii字符串-如果长度为
15
,则将发送字符
1
(代码ascii
0x31
)和
5
(代码ascii
0x35

因此,你必须:

  • 以可移植的方式发送二进制长度(C或C++中有<代码> HOT和<代码> Ntoh < /Cord>,但java中不确定)
  • > P>在java端文本长度之后添加分隔符(新行),并在C++中解码:

    char buffer[1024]; // a size big enough to read the packet
    int iDataLength, l;
    l = recv(client, (char *)&iDataLength, sizeof(iDataLength), 0);
    if (l != SOCKET_ERROR) {
        buffer[l] = 0;
        iDataLength = sscanf(buffer, "%d", &iDataLength);
        char *ptr = strchr(buffer, '\n');
        if (ptr == NULL) {
             // should never happen : peer does not respect protocol
             ...
        }
        ptr += 1; // ptr now points after the length
        //do something
    }
    
    Java部分应该是:
    clientSender.println(text.length());

编辑:


根据Remy Lebeau的评论,TCP中的发送和读取之间没有1对1关系。recv()可以并且确实返回任意数量的数据,因此不能假设单个recv()将读取整行文本


上面的代码不应该执行简单的
recv
,而应该准备好连接多个读取以找到分隔符(留给读者的练习是:-)

数值和ASCII表示之间存在混淆

在Java中编写
clientSender.print(text.length());
实际上是在编写ascii字符串-如果长度为
15
,则将发送字符
1
(代码ascii
0x31
)和
5
(代码ascii
0x35

因此,你必须:

  • 以可移植的方式发送二进制长度(C或C++中有<代码> HOT和<代码> Ntoh < /Cord>,但java中不确定)
  • > P>在java端文本长度之后添加分隔符(新行),并在C++中解码:

    char buffer[1024]; // a size big enough to read the packet
    int iDataLength, l;
    l = recv(client, (char *)&iDataLength, sizeof(iDataLength), 0);
    if (l != SOCKET_ERROR) {
        buffer[l] = 0;
        iDataLength = sscanf(buffer, "%d", &iDataLength);
        char *ptr = strchr(buffer, '\n');
        if (ptr == NULL) {
             // should never happen : peer does not respect protocol
             ...
        }
        ptr += 1; // ptr now points after the length
        //do something
    }
    
    Java部分应该是:
    clientSender.println(text.length());

编辑:


根据Remy Lebeau的评论,TCP中的发送和读取之间没有1对1关系。recv()可以并且确实返回任意数量的数据,因此不能假设单个recv()将读取整行文本


上面的代码不应该执行简单的
recv
,而是准备好连接多个读取以找到分隔符(留给读者的练习:-))

简单解释-使用具有明确的逐字节表示的协议,以便使endianness、alignment、delivery等不相关且始终有效。我认为您正在尝试将数字的文本表示形式直接转换为
int
。可能将其读入字符缓冲区,然后使用类似
std::stoi()的内容
。以下是
PrintWriter
对您的
int
所做的操作:“由string.valueOf(int)生成的字符串被转换为字节”。您需要将其从platform endian转换为network endian。然后,您需要确保您发送的任何数据在另一端的大小相同。在这里,您可以将其转换回platform endian。这并不像看上去那么简单。最好使用类似JSON的文本协议。简单解释-使用具有明确逐字节表示的协议,以便dianness、校准、交付