отправлять и получать JSON через сокеты на сервере и клиентском приложении в C

Я хочу отправить данные в JSON поверх сокетов в приложении клиент-сервер, написанном на C.

Я использую библиотеку jsonc / libjson для обработки данных JSON в приложении C.

Работая над некоторыми учебными материалами, я могу создать объект 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_object jobj с клиента на сервер. Как это сделать?

Некоторые вещи, которые я пробовал:

  1. Когда я использую write(fd, jobj, sizeof(jobj)) , клиент отправляет только 8 байт, а другие символы имеют значение null, когда я получаю данные на сервере.

  2. Я могу отправить полную json_object jobj, когда я использую write(fd, json_object_to_json_string(jobj), 157) где 157 – общее количество символов в этом объекте jobj.

  3. На стороне сервера, если я использую read(connfd, jobj, sizeof(jobj)) тогда я могу получить только 8 байтов (на стороне клиента я использую write(fd, jobj, sizeof(jobj)) ).

  4. Если я использую вышеуказанный server.c, я могу получить полный объект JSON (если я уже знаю количество символов в этом объекте). Но он находится в необработанном формате (прием байта за байтом).

Как отправить полную json_object jobj с клиентской стороны?

И как получить полную json_object jobj на стороне сервера?

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  #include  #include  #include  #include  #include  #include  #include  #include  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  #include  #include  #include  #include  #include  #include  #include  #include  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 ... ) и отступьте его правильно.

Чтобы использовать сообщения сокета TCP, клиент и сервер узнают, как интерпретировать сообщения.

HTTP, например, использует 2 символа \ r \ n в качестве конца сообщения. Другой альтернативный вариант – отправить размер сообщения перед сообщением.

Вот решение, использующее библиотеку JSON Jansson. Он добавляет размер как последовательность символов (например, «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(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(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