【问题标题】:How can I pass integer through the buffer?如何通过缓冲区传递整数?
【发布时间】:2017-01-21 07:52:03
【问题描述】:

我想创建一个可以读取一些数字而不是字符串的套接字,以便我可以对它们执行一些操作。 如何发送整数而不是字符串 这是我目前的程序:

  int main(int argc, char *argv[])
{ 
int sockfd, newsockfd, port, clilen;
struct sockaddr_in serv_addr, cli_addr;

if (argc < 2)
error("ERROR, no port provided\n");
port = atoi(argv[1]);

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");

bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port); //host to network

if (bind(sockfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR binding to socket");

listen(sockfd,2);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,(struct sockaddr *)&cli_addr, &clilen);
int n;
void* buffer[256];
n = read(newsockfd,buffer,255);


if (n < 0) error("ERROR reading from socket");
printf("Message received:%d\n",d);

n = write(newsockfd,buffer, strlen((char *)buffer));
if (n < 0)
error("ERROR writing back to socket");

return 0;}

我试过这个:

int n= 5;
 write(newsockfd,&n, sizeof(int));

但在客户端(也是一个 void* 缓冲区),它似乎没有读取任何内容。谁能帮我解决这个问题?

一种解决方案可能是传递字符串然后转换为 int 等。但这是正确的方法吗?

【问题讨论】:

  • 为了便于阅读和理解:1) 遵循公理:每行只有一个语句,并且(最多)每个语句有一个变量声明。 2) 始终缩进代码:在每个(甚至可选的)左大括号'{'之后缩进。在每个(甚至是可选的)右大括号 '}' 之前不缩进。
  • 函数:listen()accept() 需要检查它们的返回值以确保操作成功。
  • 发布的代码无法编译。除其他外,它缺少所有需要的#include 语句。当问一个各种各样的问题时:why doesn't my code work,干净编译后的代码很小,但仍然显示问题。
  • 假设代码正在接收一个二进制数(可能是 4 或 8 个字节)。这样的数字可以有 NUL 字节。例如,数字 1 将是:0000000101000000,具体取决于发送者的字节序。因此,此语句:n = write(newsockfd,buffer, strlen((char *)buffer)); 将不起作用。建议:n = write(newsockfd,buffer, n));
  • 发布的代码使用变量d,但该变量未在发布的代码中的任何地方定义

标签: c sockets client-server


【解决方案1】:

问题有两个方面:

第一个问题是声明 void* buffer[256] 声明 buffer 是一个由 256 个指向 void 的指针组成的数组,我猜这并不是真正的意图。相反,如果您想要一个 256 字节的数组,只需执行 char buffer[256]

第二个问题是在接收端你把数据当作一个字符串,但它不是一个字符串而是一个二进制数据值。

第二个问题可以通过两种方式解决:将整数转换为字符串,然后发送该字符串。或者您收到整数的原始数据,并将其转换为int。第一个解决方案涉及sprintf,另一个解决方案涉及memcpy 或指针和强制转换。


对于上面的第一个解决方案,您可以这样做:

char temp[32];
snprintf(temp, sizeof temp, "%d", n);
write(newsockfd, temp, sizeof temp);

在接收端,您将其读取为字符串:

char buffer[32];
n = read(newsockfd, buffer, sizeof buffer);
if (n <= 0)
{
    // Error or connection closed
}

// TODO: Since TCP is a streaming protocol, we could actually receive
//       less than the number of bytes we asked to read, so we need
//       to read in a loop

// Print the received data as a string
printf("Received '%s'\n", buffer);  // Works because the sender sent with the terminator

// Convert to integer
int i = strtol(buffer, NULL, 10);
printf("Received %d\n", i);

对于上面的第二种解决方案,将整数值作为原始二进制数据发送,就像你已经做的那样,但是当接收到这个时,做一些类似的事情

char buffer[32];
n = read(newsockfd, buffer, sizeof buffer);
if (n <= 0)
{
    // Error or connection closed
}

// TODO: Since TCP is a streaming protocol, we could actually receive
//       less than the number of bytes we asked to read, so we need
//       to read in a loop

int i = *(int *) buffer;
printf("Received %d\n", i);

请注意,第二个解决方案,正如它在这个答案中所写的那样,依赖于发送者和接收者都具有相同的endianness,以及int 的相同大小(可能并非总是如此)。

最安全的方法通常是在发送端转换为字符串,然后将字符串作为字符串接收。 IE。第一个解决方案。

【讨论】:

  • 同意最安全的方式一般是转换成字符串,但是这段代码不发送单个字符串,而是数组内容肯定是空终止的,而接收端却不“知道”长度的数组。 OTOH,如果两端都“知道”缓冲区大小是 31+1,那么为什么不“知道”int 大小呢?更简单。为了“安全”地传输数据,不要依赖像 32 这样的幻数。发送 strings 时,还要发送最后的空字符。否则先发送尺寸信息,然后再发送数据。
【解决方案2】:

我想修改提供的清晰答案Joachim Pileborg

int value, todo = sizeof(value);
char *p = (void*) &value;

while ( (n = read(newsockfd, p, todo)) < todo ) {
  if( n == 0 ) { /* handle eof and break */ }
  if( n < 0  ) { /* handle error and break */ }

  todo -= n;
  p += n
}

if( todo == 0 ) {
  printf("Received %d\n", value);
}

您知道收到了多少字节,因此没有理由阅读更多内容。该循环处理他警告过的情况: read(2) 可能无法读取所有 4 个字节(在这种情况下不太可能,但通常不会)。

您始终可以从void * 获取char *,并将任何指针传递给接受void * 的函数。你不能便携地做的是

char buffer[32];
...
int i = *(int *) buffer;

因为不保证字符数组具有整数对齐。在某些架构上,这会引发 SIGBUS。如果您读入字符数组,将这些字节解释为整数的安全方法是

memcpy(&i, buffer, sizeof i);

【讨论】:

    猜你喜欢
    • 2012-08-04
    • 2015-12-09
    • 1970-01-01
    • 2013-06-14
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多