Convertir un uint16_t a char[2] para ser enviado por socket (unix)

5 minutos de lectura

Avatar de usuario de Michael Crook
miguel ladrón

Sé que hay cosas por ahí más o menos sobre esto… Pero me duele el cerebro y no puedo encontrar nada para hacer que esto funcione…

Estoy tratando de enviar un entero sin firmar de 16 bits a través de un socket Unix. Para hacerlo, necesito convertir un uint16_t en dos caracteres, luego necesito leerlos en el otro extremo de la conexión y convertirlo nuevamente en un int sin firmar o un uint16_t, en ese momento no importa si usa 2 bytes o 4 bytes (estoy ejecutando 64 bits, es por eso que no puedo usar int sin firmar 🙂

Estoy haciendo esto en C por cierto

Gracias

  • Lo intentaré cuando llegue a casa de la universidad :\ este semestre ha sido un infierno con todo yendo terrible… Tengo tantas extensiones, ahora todas mis tareas están en el receso de estudio… Lástima que el estudio no pueda obtener una extension =.= gotta goto uni para una discusión de revisión de física Gracias a todos por las respuestas rápidas

    –Michael Crook

    8 de noviembre de 2012 a las 5:21

  • ¿Estás seguro de que quieres usar carbón simple y no carácter sin firmar – o mejor uint8_t?

    – Andrés

    8 de noviembre de 2012 a las 6:31

Avatar de usuario de Steven Sudit
Steven Sudit

¿Por qué no simplemente dividirlo en bytes con máscara y desplazamiento?

 uint16_t value = 12345;
 char lo = value & 0xFF;
 char hi = value >> 8;

(editar)

En el otro extremo, se ensambla con el reverso:

 uint16_t value = lo | uint16_t(hi) << 8;

Fuera de mi cabeza, no estoy seguro de si se requiere ese yeso.

  • Bueno, esta respuesta me ahorró mucho dolor, incluso después de que la usé más de un año después de que la escribieras. Gracias.

    –Marek

    7 sep 2013 a las 13:47

  • y un año después sigue siendo útil, así que no dejéis de difundir buena onda

    – ruisea

    21 de febrero de 2014 a las 23:49

  • lo siento todo, me olvidé de la pregunta… Estaba haciendo una limpieza de primavera en mi perfil y me di cuenta de esto y me volví loco 🙁 Le di a esta la respuesta correcta desde hace un millón de años que hice esto, esto me suena como lo hice… Además, a todo el mundo parece gustarle la respuesta. Lo siento @StevenSudit

    –Michael Crook

    9 de octubre de 2014 a las 2:59

  • Probablemente estoy haciendo esto mal, pero lo contrario no funciona para números grandes como 36345 a menos que lance lo a un uint8_t primero. uint16_t value = (uint8_t)lo | hi << 8;

    – Teynon

    22 de enero de 2021 a las 4:14

Avatar de usuario de Goz
Goz

char* pUint16 = (char*)&u16;

es decir, emita la dirección del uint16_t.

char c16[2];
uint16_t ui16 = 0xdead;
memcpy( c16, ui16, 2 );

c16 ahora contiene los 2 bytes de u16. En el otro extremo, simplemente puede invertir el proceso.

char* pC16 = /*blah*/
uint16_t ui16;
memcpy( &ui16, pC16, 2 );

Curiosamente, aunque hay una llamada a memcpy, casi todos los compiladores lo optimizarán porque tiene un tamaño fijo.

Como señala Steven sudt, puede tener problemas con big-endian-ness. para evitar esto, puede usar la función htons (host-to-network short).

uint16_t ui16correct = htons( 0xdead );

y en el otro extremo use ntohs (corto de red a host)

uint16_t ui16correct = ntohs( ui16 );

En una máquina little-endian, esto convertirá el short a big-endian y luego, en el otro extremo, volverá a convertirlo de big-endian. En una máquina big-endian, las 2 funciones no hacen nada.

por supuesto si tu saber que la arquitectura de ambas máquinas en la red use el mismo endian-ness, entonces puede evitar este paso.

Busque ntohl y htonl para manejar enteros de 32 bits. La mayoría de las plataformas también admiten ntohll y htonll para 64 bits.

  • Ok, esto le dará un puntero de caracteres en el int16, pero lo que encuentre en el primer elemento dependerá de big-endian vs. small-endian.

    – Steven Sudit

    7 de noviembre de 2012 a las 22:10

  • Tienes razón en que memcpy generalmente se optimizará, pero esto es aún sistema dependiente de extremos grandes/pequeños.

    – Steven Sudit

    7 de noviembre de 2012 a las 22:12

  • Ok, eso es mejor, pero parece que ntohs() es una gran sobrecarga para algo que es bastante simple.

    – Steven Sudit

    7 de noviembre de 2012 a las 22:25

  • realmente no es… en muchas plataformas es una sola instrucción. Por otro lado, es solo un modificador de instrucciones … No me preocuparía, tbh.

    – Goz

    7 de noviembre de 2012 a las 22:27

  • @StevenSudit La sobrecarga de llamadas a la función es 0 si la función se integra, y cualquier compilador razonable integrará una función de una sola línea, no es “enorme”.

    – Étienne

    10 de febrero de 2014 a las 16:01

Parece que necesitas usar el máscara de bits y operadores de desplazamiento.

Para dividir un número de 16 bits en dos números de 8 bits:

  • enmascara los 8 bits inferiores usando el operador AND bit a bit (& en C) para que los 8 bits superiores se conviertan en 0, y luego asigne ese resultado a un carácter.
  • desplaza los 8 bits superiores a la derecha usando el operador de desplazamiento a la derecha (>> en C) de modo que los 8 bits inferiores se extraigan del entero, dejando solo los 8 bits superiores, y asígnelos a otro carácter.

Luego, cuando envía estos dos caracteres a través de la conexión, hace lo contrario: cambia lo que solían ser los 8 bits superiores hacia la izquierda 8 bits y luego usa OR bit a bit para combinar eso con los otros 8 bits.

Avatar de usuario de Gianluca Ghettini
Gianluca Guettini

Básicamente, está enviando 2 bytes por el zócalo, eso es todo lo que el zócalo necesita saber, independientemente de la endianidad, la firma, etc., simplemente descomponga su uint16 en 2 bytes y envíelos por el zócalo.

char byte0 = u16 & 0xFF;
char byte1 = u16 >> 8;

En el otro extremo haz la conversión en sentido contrario.

¿Ha sido útil esta solución?