IP is the Internet Protocol, which defines how to talk to computers on the Internet.

The two ways you can send data over it are the TCP protocol, and the UDP protocol.

TCP is for reliable, ordered and stream-based transport, UDP is for sending packets of data over IP without these guarantees.

To create a UDP socket, you use the socket(2) system call, with the AF_INET and SOCK_DGRAM options.

int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);

To recieve packets you need to bind(2) the socket, as you would a TCP socket.

int ret = bind(udp_socket,
               (struct sockaddr*)(&(struct sockaddr_in){
                   .sin_family=AF_INET,
                   .sin_port=htons(1234),
                   .sin_addr=htonl(INADDR_ANY),
               }),
               sizeof(struct sockaddr_in));
if (ret != 0) return 1;

You don't need to call connect(2) to send messages over UDP, you then just call sendto(2).

ssize_t ret = sendto(udp_socket,
                     argv[0], strlen(argv[0]), 0,
                     (struct sockaddr*)(&(struct sockaddr_in){
                         .sin_family=AF_INET,
                         .sin_port=htons(1234),
                         .sin_addr=htonl(inet_addr("127.0.0.1")),
                     }),
                     sizeof(struct sockaddr_in));

Now on the recieving end, you can use recvfrom(2) to read the packet.

You need to specify a buffer to write the message to, and how large that buffer is. If your protocol doesn't define ahead of time how large that buffer is, you can query this with MSG_PEEK|MSG_TRUNC:

ssize_t msgsize = recvfrom(udp_socket, NULL, 0, MSG_PEEK|MSG_TRUNC,
                           NULL, NULL);
char *msgbuf = malloc(msgsize);

You can also get the address of the sender of the packet by providing src_addr and addrlen to the recvfrom(2) call.

struct sockaddr_in recvaddr;
socklen_t recvaddrlen = sizeof(recvaddr);
(void) recvfrom(udp_socket, msgbuf, msgsize, 0,
                (struct sockaddr*)&recvaddr, &recvaddrlen);

You now have all the data of the message, which may have been variable length, and know where it came from.

You could use this to send a reply to the sender, or just log it as follows:

printf("%zd bytes containing %s from %s:%d\n",
       msgsize, msgbuf,
       inet_ntoa(recvaddr.sin_addr),
       recvaddr.sin_port);

Program listings for the client and server can be read at udp-server.c and udp-client.c.