web-dev-qa-db-ja.com

サーバーのソケットとCのクライアントアプリケーションを介してJSONを送受信します

Cで記述されたサーバークライアントアプリケーションのソケットを介してJSONでデータを送信したいと思います。

CアプリケーションでJSONデータを処理するために json-c/libjson ライブラリを使用しています。

いくつかのチュートリアルに取り組むことで、JSONオブジェクトを作成し、正常に解析することができます。

ここで、サーバークライアントの通信にJSONデータ形式を使用したいと思います。

これが私のサーバーとクライアントのコードの一部です

server.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, '\0', 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;
}
_

Json_objectjobjをクライアントからサーバーに送信したいと思います。これを行う方法?

私が試したいくつかのこと:

  1. write(fd, jobj, sizeof(jobj))を使用すると、クライアントは8バイトのみを送信し、サーバーでデータを受信すると他の文字はnullになります。

  2. write(fd, json_object_to_json_string(jobj), 157)を使用した場合にのみ、完全なjson_object jobjを送信できます。ここで、157はそのjobjオブジェクトの合計文字数です。

  3. サーバー側でread(connfd, jobj, sizeof(jobj))を使用すると、8バイトしか受信できません(クライアント側ではwrite(fd, jobj, sizeof(jobj))を使用しています)。

  4. 上記のserver.cを使用すると、完全なJSONオブジェクトを受け取ることができます(そのオブジェクトの文字数がすでにわかっている場合)。しかし、それは生のフォーマットです(バイトごとに受け取ります)。

クライアント側から完全なjson_objectjobjを送信するにはどうすればよいですか?

そして、サーバー側で完全なjson_object jobjを受け取る方法は?

6
Edge Goldberg

1)jobjはポインタなので、

write(fd, jobj, sizeof(jobj))

ポインタをそのオブジェクトではなくそのオブジェクトに書き込んでいます。

3)以前と同じ。

多分あなたはそれを次のようなもので送ってみるべきです

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

受信側では、次のようにforループを実行する必要があります。

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

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

jsonの最大サイズがわかっている場合、またはmalloc()realloc()それ以外の場合

編集:

私はこれをしました、そして今それはうまく働きます。

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("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;
}

server.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;
}

MAX_SIZEは、最大バッファー長を指定するマクロです。必要に応じて設定してください。次回は、すべてのコード(#include ...を含む)を貼り付けて、適切にインデントしてください。

4
igng

TCPソケットメッセージを使用するには、クライアントとサーバーにメッセージの解釈方法を学習させます。

たとえば、HTTPはメッセージの終わりとして2文字\ r\nを使用します。別の代替オプションは、メッセージの前にメッセージのサイズを送信することです。

これは、JSONJanssonライブラリを使用してそれを行うソリューションです。サイズを文字のシーケンスとして追加し(たとえば、「123」、後で整数123として解析されます)、サイズヘッダーの最後に文字「#」を追加してから、JSONオブジェクトのJSONテキスト表現(Janssonライブラリで定義)を追加します。 )

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();
}

読書はで行われます

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] = '\0';
  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;
}

これはで実装されています

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

0
Pedro Vicente