less_retarded_wiki/network.md
2024-05-28 21:17:58 +02:00

4.5 KiB

Network

TODO

Code Examples

First let's try writing some UDP C program under Unix. Remember that UDP is the unreliable protocol, so it's possible our messages may get lost or distorted, but in programs that can handle some losses this is the faster and more KISS way. Our program will be peer-to-peer, it will create two sockets, one listening and one sending. It will make a few message exchange turns, in each turn it will send something to its partner, it will check if it itself got any message and then will wait for some time before the next round. Note that we will use a non-blocking receiving socket, i.e. checking if we have any messages won't pause our program if there is nothing to be received, we'll simply move on if there is nothing (that's how realtime games may do it, but other kinds of server may rather a use blocking socket if they intend to do nothing while waiting for a message). Also pay attention to the fact that the program will choose its port number based on a one letter "name" we give to the program -- this is so that if we test the programs on the same computer (where both will have the same IP address), they will choose different ports (different processes on the same computer cannot of course use the same port).

#include <stdio.h>
#include <stdlib.h>            // for exit
#include <unistd.h>            // for sleep

#include <arpa/inet.h>
#include <sys/socket.h>

#define BUFFER_LEN 8
#define PORT_BASE 1230

// run as ./program partner_addr partner_letter my_letter

char buffer[BUFFER_LEN + 1];   // extra space for zero terminator
char name;                     // name of this agent (single char)
int sock = -1;                 // socket, for both sending and receiving

void error(const char *msg)
{
  printf("%c: ERROR, %s\n",name,msg);

  if (sock >= 0)
    close(sock);

  exit(1);
}

int main(int argc, char **argv)
{
  if (argc < 4)
    error("give me correct arguments bitch");

  name = argv[3][0];
  char *addrStrDst = argv[1];
  int portSrc = PORT_BASE + name, // different name => different port
      portDst = PORT_BASE + argv[2][0];

  struct sockaddr_in addrSrc, addrDst;

  sock = socket(AF_INET,SOCK_DGRAM | SOCK_NONBLOCK,IPPROTO_UDP);

  if (sock < 0)
    error("couldn't create socket");

  addrSrc.sin_family = AF_INET;
  addrSrc.sin_port = htons(portSrc); // convert port to netw. endianness
  addrSrc.sin_addr.s_addr = htonl(INADDR_ANY);

  if (bind(sock,(struct sockaddr *) &addrSrc,sizeof(addrSrc)) < 0)
    error("couldn't bind socket");

  addrDst.sin_family = AF_INET;
  addrDst.sin_port = htons(portDst);

  if (inet_aton(addrStrDst,&addrDst.sin_addr) == 0)
    error("couldn't translate address");

  printf("%c: My name is %c, listening on port %d, "
    "gonna talk to %c (address %s, port %d).\n",
    name,name,portSrc,argv[2][0],addrStrDst,portDst);

  for (int i = 0; i < 4; ++i)
  {
    printf("%c: Checking messages...\n",name);

    int len = recv(sock,buffer,BUFFER_LEN,0);

    if (len > 0)
    {
      buffer[len] = 0;
      printf("%c: Got \"%s\"\n",name,buffer);
    }
    else
      printf("%c: Nothing.\n",name);

    for (int j = 0; j < BUFFER_LEN; ++j) // make some gibberish message
      buffer[j] = 'a' + (name + i * 3 + j * 2) % 26;
 
    printf("%c: Sending \"%s\"\n",name,buffer);

    if (sendto(/*sockOut*/sock,buffer,BUFFER_LEN,0,
      (struct sockaddr *) &addrDst,sizeof(addrDst)) < 0)
      printf("%c: Couldn't send it!\n",name);

    printf("%c: Waiting...\n",name);
    usleep(2000000); 
  }

  printf("%c: That's enough, bye.\n",name);

  close(sock);

  return 0;
}

We can test this for example like this:

./program 127.0.0.1 A B & { sleep 1; ./program 127.0.0.1 B A; } &

Which may print out something like this:

B: My name is B, listening on port 1296, gonna talk to A (address 127.0.0.1, port 1295).
B: Checking messages...
B: Nothing.
B: Sending "oqsuwyac"
B: Waiting...
A: My name is A, listening on port 1295, gonna talk to B (address 127.0.0.1, port 1296).
A: Checking messages...
A: Nothing.
A: Sending "nprtvxzb"
A: Waiting...
B: Checking messages...
B: Got "nprtvxzb"
B: Sending "rtvxzbdf"
B: Waiting...
A: Checking messages...
A: Got "rtvxzbdf"
A: Sending "qsuwyace"
A: Waiting...
B: Checking messages...
B: Got "qsuwyace"
B: Sending "uwyacegi"
B: Waiting...
A: Checking messages...
A: Got "uwyacegi"
A: Sending "tvxzbdfh"
A: Waiting...
B: Checking messages...
B: Got "tvxzbdfh"
B: Sending "xzbdfhjl"
B: Waiting...
A: Checking messages...
A: Got "xzbdfhjl"
A: Sending "wyacegik"
A: Waiting...
B: That's enough, bye.
A: That's enough, bye.

TODO: TCP