C语言 通过 C 语言中的服务器和客户端应用程序中的套接字发送和接收 JSON

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32667109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 12:08:28  来源:igfitidea点击:

send and receive JSON over sockets in server and client application in C

cjsonsockets

提问by Edge Goldberg

I want to send the data in JSON over sockets in a server-client application written in C.

I am using json-c / libjsonlibrary for handling JSON data in C application.

我想在用 C 编写的服务器客户端应用程序中通过套接字发送 JSON 中的数据。

我正在使用json-c/libjson库来处理 C 应用程序中的 JSON 数据。

By working on some of the tutorials I am able to create JSON object and able to parse it successfully.

通过学习一些教程,我能够创建 JSON 对象并成功解析它。

Now I want to use the JSON data format for the communication of server-client.

现在我想使用 JSON 数据格式进行服务器 - 客户端的通信。

Here is part of my server and client code

这是我的服务器和客户端代码的一部分

server.c

服务器.c

int main()
{
   int listenfd = 0, connfd = 0;    //related with the server
   struct sockaddr_in serv_addr;

   //json_object * jobj;
   uint8_t buf[158], i;

   memset(&buf, '0', sizeof(buf));
   listenfd = socket(AF_INET, SOCK_STREAM, 0);

   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   serv_addr.sin_port = htons(8888); 

   bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
   printf("binding\n");

   listen(listenfd, 5);
   printf("listening\n");
   connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

   printf("Reading from client\n");

   while ( (read(connfd, buf, 157)) > 0 )
   {
      for ( i = 0; i < 157; i++ )
         //printf("%s\n", json_object_to_json_string(jobj));
         //json_parse(jobj);
         printf("%d\n", buf[i]);
   }

   return 0;
}

client.c

客户端

int main()
{ 
    char* str;
    int fd = 0;
    struct sockaddr_in demoserverAddr;

    fd = socket(AF_INET, SOCK_STREAM, 0);

    if (fd < 0)
    {
        printf("Error : Could not create socket\n");
        return 1;
    }
    else
    {
        demoserverAddr.sin_family = AF_INET;
        demoserverAddr.sin_port = htons(8888);
        demoserverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        memset(demoserverAddr.sin_zero, '
write(fd, jobj, sizeof(jobj))
', sizeof(demoserverAddr.sin_zero)); } if (connect(fd, (const struct sockaddr *)&demoserverAddr, sizeof(demoserverAddr)) < 0) { printf("ERROR connecting to server\n"); return 1; } /*Creating a json object*/ json_object *jobj = json_object_new_object(); /*Creating a json string*/ json_object *jstring = json_object_new_string("Joys of Programming"); /*Creating a json integer*/ json_object *jint = json_object_new_int(10); /*Creating a json boolean*/ json_object *jboolean = json_object_new_boolean(1); /*Creating a json double*/ json_object *jdouble = json_object_new_double(2.14); /*Creating a json array*/ json_object *jarray = json_object_new_array(); /*Creating json strings*/ json_object *jstring1 = json_object_new_string("c"); json_object *jstring2 = json_object_new_string("c++"); json_object *jstring3 = json_object_new_string("php"); /*Adding the above created json strings to the array*/ json_object_array_add(jarray,jstring1); json_object_array_add(jarray,jstring2); json_object_array_add(jarray,jstring3); /*Form the json object*/ /*Each of these is like a key value pair*/ json_object_object_add(jobj,"Site Name", jstring); json_object_object_add(jobj,"Technical blog", jboolean); json_object_object_add(jobj,"Average posts per day", jdouble); json_object_object_add(jobj,"Number of posts", jint); json_object_object_add(jobj,"Categories", jarray); printf("Size of JSON object- %lu\n", sizeof(jobj)); printf("Size of JSON_TO_STRING- %lu,\n %s\n", sizeof(json_object_to_json_string(jobj)), json_object_to_json_string(jobj)); //printf("Size of string- %lu\n", sizeof(json_object_to_json_string(jobj))); write(fd, json_object_to_json_string(jobj), 157); printf("Written data\n"); return 0; }

I want to send the json_object jobj from client to server. How to do this?

我想将 json_object jobj 从客户端发送到服务器。这该怎么做?

Some things that I tried:

我尝试过的一些事情:

  1. When I use write(fd, jobj, sizeof(jobj)), the client sends only 8 bytes and other characters are null when I receive data on server.

  2. I am only able to send complete json_object jobj when I use write(fd, json_object_to_json_string(jobj), 157)where 157 is the total number of characters in that jobj object.

  3. On server side if I use read(connfd, jobj, sizeof(jobj))then I only able to receive 8 bytes (on client side I am using write(fd, jobj, sizeof(jobj))).

  4. If I use the above server.c, I am able to receive complete JSON object (if I already know the number of characters in that object). But it is in raw format (receiving byte by byte).

  1. 当我使用时write(fd, jobj, sizeof(jobj)),当我在服务器上接收数据时,客户端只发送 8 个字节,其他字符为空。

  2. 当我使用write(fd, json_object_to_json_string(jobj), 157)157 是该 jobj 对象中的总字符数时,我只能发送完整的 json_object jobj 。

  3. 在服务器端,如果我使用,read(connfd, jobj, sizeof(jobj))那么我只能接收 8 个字节(在客户端我正在使用write(fd, jobj, sizeof(jobj)))。

  4. 如果我使用上面的 server.c,我能够接收完整的 JSON 对象(如果我已经知道该对象中的字符数)。但它是原始格式(逐字节接收)。

How to send the complete json_object jobj from client side?

如何从客户端发送完整的 json_object jobj?

And how to receive the complete json_object jobj on server side?

以及如何在服务器端接收完整的 json_object jobj?

采纳答案by igng

1) jobj is a pointer, so when you use

1) jobj 是一个指针,所以当你使用

if (write(fd, jobj, sizeof(*jobj)) == -1)
    /* error handler /*

you're writing a pointerto that object, not that object.

您正在写入指向该对象的指针,而不是该对象。

3) Same as before.

3)和以前一样。

Maybe you should try sending it with something like

也许你应该尝试用类似的东西发送它

for (;;)
{
    r = read(fd, jobj, SIZE);

    if (r == -1)
        /*error handler*/
    if (r == 0)
        break;
}

On the receive side, you should do a for loop, like

在接收端,你应该做一个 for 循环,比如

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <json/json.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
    /* all previous code until
    printf("Size of string- %lu\n", sizeof(json_object_to_json_string(jobj)))*/

    char temp_buff[MAX_SIZE];

    if (strcpy(temp_buff, json_object_to_json_string(jobj)) == NULL)
    {
        perror("strcpy");
        return EXIT_FAILURE;
    }

    if (write(fd, temp_buff, strlen(temp_buff)) == -1)
    {
        perror("write");
        return EXIT_FAILURE;
    }

    printf("Written data\n");
    return EXIT_SUCCESS;
}

if you know the maximum SIZE of the json, or combine malloc()and realloc()otherwise

如果你知道 json 的最大 SIZE,或者结合malloc()realloc()否则

EDIT:

编辑:

I did this, and now it works fine.

我这样做了,现在它工作正常。

client.c

客户端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <json/json.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
    /* all previous code until
    printf("Reading from client\n"); */

    ssize_t r;

    char buff[MAX_SIZE];

    for (;;)
    {
        r = read(connfd, buff, MAX_SIZE);

        if (r == -1)
        {
            perror("read");
            return EXIT_FAILURE;
        }
        if (r == 0)
            break;

        printf("READ: %s\n", buff);
    }

    return EXIT_SUCCESS;
}

server.c

服务器.c

size_t socket_t::write(json_t *json)
{
  char *buf_json = NULL;
  std::string buf_send;
  size_t size_json;

  //get char* from json_t
  buf_json = json_dumps(json, JSON_PRESERVE_ORDER);
  size_json = strlen(buf_json);
  //construct send buffer, adding a header with size in bytes of JSON and # terminator
  buf_send = std::to_string(static_cast<long long unsigned int>(size_json));
  buf_send += "#";
  buf_send += std::string(buf_json);

  this->write(buf_send.data(), buf_send.size());

  free(buf_json);
  return buf_send.size();
}

MAX_SIZE is a macro that specifies the maximum buffer length, set it as you wish. Please next time paste ALL your code (including the #include ...) and indent it properly.

MAX_SIZE 是指定最大缓冲区长度的宏,根据需要进行设置。下次请粘贴您的所有代码(包括#include ...)并正确缩进。

回答by Pedro Vicente

To use TCP socket messages you have the client and server learn how to interpret the messages.

要使用 TCP 套接字消息,您需要让客户端和服务器学习如何解释这些消息。

HTTP, for example, uses the 2 characters \r\n as end of message Another alternative option is to send the size of the message before the message.

例如,HTTP 使用 2 个字符 \r\n 作为消息的结尾 另一种选择是在消息之前发送消息的大小。

Here's a solution that does that using the JSON Jansson library. It adds the size as a sequence of characters (e.g "123", is later parsed as integer 123), adding the character "#" as end of size header then the JSON text representation of the JSON object (as defined by the Jansson library)

这是使用 JSON Jansson 库执行此操作的解决方案。它将大小添加为字符序列(例如“123”,后来被解析为整数 123),添加字符“#”作为大小标题的结尾,然后是 JSON 对象的 JSON 文本表示(由 Jansson 库定义) )

json_t * socket_t::read()
{
  int recv_size; // size in bytes received or -1 on error 
  const int size_buf = 20;
  char buf[size_buf];

  //peek header
  if ((recv_size = recv(m_socket, buf, size_buf, MSG_PEEK)) == -1)
  {
    std::cout << "recv error: " << strerror(errno) << std::endl;
  }

  //get size of JSON message
  std::string str(buf);
  size_t pos = str.find("#");
  std::string str_header(str.substr(0, pos));

  //parse header
  if ((recv_size = recv(m_socket, buf, str_header.size() + 1, 0)) == -1)
  {
    std::cout << "recv error: " << strerror(errno) << std::endl;
  }

  //sanity check
  buf[recv_size - 1] = '##代码##';
  assert(str_header.compare(buf) == 0);

  size_t size_json = static_cast<size_t>(std::stoull(str_header));
  std::string str_buf = read_all(size_json);
  //terminate buffer with size
  std::string str_json = str_buf.substr(0, size_json);

  //construct JSON
  json_error_t *err = NULL;
  json_t *json = json_loadb(str_json.data(), str_json.size(), JSON_PRESERVE_ORDER, err);
  return json;
}

The reading is done with

阅读完成

##代码##

This is implemented in

这是在

https://github.com/pedro-vicente/lib_netsockets

https://github.com/pedro-vicente/lib_netsockets