【问题标题】:C-Server Socket become wrong PacketC-Server Socket 变错 Packet
【发布时间】:2016-11-03 09:00:55
【问题描述】:

我有以下问题:

  • 我有一个使用 apache mina 框架发送字符串的 Java 客户端。
  • 我写了一个C服务器来接收字符串。
  • 所以,我尝试向服务器发送一个 utf8 字符:myStringToSend = "Ä"
  • 我调试了 mina 源以查看字节或十六进制的数据包,并且 mina 很好地转换了字符串,因此它发送了 0xC3 0x84
  • 我还在wireshark网络查看器中检查了发送的数据包,它还看到0xC30x84作为数据包,一切正常。
  • 但我的 C 服务器收到以下字节:FFFFFFC3FFFFFF84

不知道怎么回事?

我的 C-Server 的代码:

#include "stdafx.h"
#include <io.h>
#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

#define DEFAULT_BUFLEN 512
#define TOOBIG 100000

int main(int argc, char *argv[])
{
WSADATA wsa;
int i, c, iResult, iSendResult, outputCounter;

SOCKET listenSocket = INVALID_SOCKET, clientSocket = INVALID_SOCKET;
struct sockaddr_in server, client; 
char *message;

char sendbuf[DEFAULT_BUFLEN];
unsigned char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;

printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != NO_ERROR)
{
    printf("Failed. Error Code : %d",WSAGetLastError());
    return 1;
}
printf("Initialised.\n");     

if((listenSocket = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP )) == INVALID_SOCKET) //IPv4, TCP
{
    printf("Could not create socket : %d" , WSAGetLastError());
    WSACleanup();
    return 1;
}

printf("Socket created.\n");

memset(&server, '0', sizeof(server));
memset(sendbuf, '0', sizeof(sendbuf)); 

server.sin_family = AF_INET;
//server.sin_addr.s_addr = htonl(0x7F000001); //localhost - 127.0.0.1
server.sin_addr.s_addr = inet_addr("127.0.0.1");
//server.sin_addr.s_addr = inet_addr("10.48.53.166");
server.sin_port = htons(7368); 

if( bind(listenSocket ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
{
    printf("Bind failed with error code : %d" , WSAGetLastError());
    closesocket(listenSocket);
    WSACleanup();
    return 1;
}

puts("Bind done");

listen(listenSocket, 10); 

//Accept and incoming connection
puts("Waiting for incoming connections...");


c = sizeof(struct sockaddr_in);

message = "{\"id\":2,\"result\":{\"code\":\"999\",\"message\":\"AddPageRange StartIndex don't match Inspection Counter\"},\"Client_ID\":\"PQCS1\",\"jsonrpc\":\"2.0\",\"Protocol\":\"2H\"}";

while( (clientSocket = accept(listenSocket , (struct sockaddr *)NULL, NULL)) != INVALID_SOCKET )
{
    puts("Connection accepted");        
    do {
        iResult = recv(clientSocket, recvbuf, recvbuflen, 0);
        if ( iResult > 0 )
        {
            printf("Bytes received: %d\n", iResult);
            recvbuf[iResult] = '\0';
            outputCounter = 0;
            while(recvbuf[outputCounter] != '\0')
                printf("%X", (unsigned char)recvbuf[outputCounter++]);              
            iSendResult = send( clientSocket, message, strlen(message), 0 );
            if (iSendResult == SOCKET_ERROR)
            {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(clientSocket);
                WSACleanup();
                return 1;
            }
            printf("Bytes sent: %d\n", iSendResult);
        }
        else if ( iResult == 0 )
            printf("Connection closed\n");
        else
            printf("recv failed: %d\n", WSAGetLastError());
    } while( iResult > 0 );

 }
if (clientSocket == INVALID_SOCKET)
{
    printf("accept failed with error code : %d" , WSAGetLastError());
    return 1;
}
closesocket(listenSocket);
WSACleanup();

return 0;
}

【问题讨论】:

  • 对于sendbuf[]recvbuf[],最好使用unsigned char 而不是char

标签: java c sockets utf-8


【解决方案1】:

您的 recvbuf 是 char 类型,怀疑是在您的平台上签名的。您的消息的两个字节都 > 127(十进制),因此被视为负数。 printf("%X") 格式说明符需要一个整数(在您的平台上看起来是 32 位),因此该数字得到符号扩展(用字节的最高有效位填充整数的最高有效 24 位) .

尝试打印出您收到了多少字节。还将您的 printf 更改为

printf("%X",  ((int)recvbuf[outputCounter++]) & 0xff);             

【讨论】:

  • 将有符号值转换为无符号值有点复杂。更简单的解决方案可能是printf("%X", (unsigned char)recvbuf[outputCounter++]); 或声明unsigned char recvbuf[DEFAULT_BUFLEN];
  • 我试图将 recvbuf 的类型更改为 unsigned char 但问题是方法 recv() 不接受 unsigned char 的这个参数。我收到一个编译错误:“无法将参数 2 从 unsigned char[512] 转换为 char*”
  • @user2963830 recv 的第二个参数是void *。它接受所有指针。所以可能你写了一些不好的东西。将修改后的代码添加到帖子中。
  • 所以我在上面添加了修改后的代码。我使用 Visual Studio,编辑器告诉我方法签名是: int_stdcall recv(SOCKET s, char *buf, int len, int flags)
  • ...好在有人记得我为什么要摆脱 msvc
【解决方案2】:

printf 有问题。

看着The Man

o、u、x、X

无符号整数参数被转换为无符号八进制 (o)、无符号十进制 (u) 或无符号十六进制(x 和 X)表示法。字母 abcdef 用于 x 转换;字母 ABCDEF 用于 X 转换。精度(如果有)给出了必须出现的最小位数;如果转换后的值需要较少的数字,则在左侧用零填充。默认精度为 1。当使用显式精度 0 打印 0 时,输出为空。

强调我的

因此,使用%X 作为格式说明符,您的数据将被提升为unsigned int,您必须仅选择要打印的字节来传递它:

printf("Byte %d: %X\n", outputCounter, recvbuf[outputCounter++] & 0xFF);

【讨论】: