Socket Programming Note

本文记录 socket programming 相关的笔记

basic

  1. Creating a socket
1
int socket(int family, int service, int protocol)
  • family: symbolic name for protocol family
    • AF_INET, AF_UNIX
  • type: symbolic name for type of service
    • SOCK_STREAM (for TCP), SOCK_DGRAM (for UDP), SOCK_RAW
  • protocol: further info in case of raw sockets
    • typically set to 0, IPPROTO_TCP, IPPROTO_UDP Returns socket descriptor

Creating a socket is in some ways similar to opening a file. This function creates a file descriptor and returns it from the function call. You later use this file descriptor for reading, writing and using with other socket functions

  1. Binding Socket with an Address
1
int bind(int sd, struct sockaddr *addr, int len)
  • sd: socket descriptor returned by socket()
  • addr: pointer to sockaddr structure containing address to be bound to socket
  • len: length of address structure Returns 0 if success, -1 otherwise

You also do not need to find information about the IP addresses associated with the host you are working on. You can specify: INNADDR_ANY to the address structure and the bind function will use on of the available (there may be more than one) IP addresses. This ensures that connections to a specified port will be directed to this socket, regardless of which Internet address they are sent to. This is useful if host has multiple IP addresses, then it enables the user to specify which IP address will be b_nded to which port number 3. Specifying Socket Address

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct sockaddr_in {
  short sin_family; /* set to AF_INET */
  u_short sin_port; /* 16 bit port number */
  struct in_addr sin_addr; /* 32 bit host address */
  char sin_zero[8]; /* not used */
};

struct in_addr {
  u_long s_addr; /* 32 bit host address */
};
  1. listen for incoming connections
1
int listen(int socket_file_descriptor, int backlog);

It is important in determining how many connections the server will connect with. Typical values for backlog are 5 – 10. The parameter socket_file_descriptor is the socket file descriptor returned by a call to socket() function. The return value of listen() is 0 for success and –1 for failure.

  1. Connecting to Server
1
int connect(int sd, struct sockaddr *addr, int len)
  • sd: socket descriptor returned by socket()
  • addr: pointer to sockaddr structure containing server’s address (IP address and port)
  • len: length of address structure Returns 0 if success, -1 otherwise
  1. Byte Ordering

Network Byte Order: Big Endian

Byte Order Conversion

  • m = ntohl(m) : network-to-host byte order, 32bit
  • m = htonl(m) : host-to-network byte order
  • ntohs, htons : short(16bit)
  1. Connection Acceptance by Server
1
int accept(int sd, struct sockaddr *from, int *len)
  • sd: socket descriptor returned by socket()
  • from: pointer to sockaddr structure which gets filled with client’s address information
  • len: length of address structure

Blocks until connection requested or error

  • returns a new socket descriptor on success accept() returns a new socket file descriptor for the purpose of reading and writing to the client.

It dequeues the next connection request on the queue for this socket of the server. If queue is empty, this function blocks until a connection request arrives

  1. More on Socket Descriptor

A 5-tuple associated with a socket

  • {protocol, local IP address, local port, remote IP address, remote port}
    • socket() fills the protocol component
    • local IP address/port filled by bind()
    • remote IP address/port by accept() in case of server
    • in case of client, both local and remote by connect()
  • Complete socket is like a file descriptor
    • Both send and recv through same socket
  • Accept returns a new complete socket
    • Original one can be used to accept more connections
  1. address convert

convert a string dotted decimal IP4 address to a NETWORK BYTE ORDERED 32 bit value: inet_addr(), inet_aton()

convert a 32 bit NETWORK BYTE ORDERED to a IP4 dotted decimal string: inet_nota()

Example

  1. Bind example
1
2
3
4
5
6
7
8
int sd;
struct sockaddr_in ma;
sd = socket(AF_INET, SOCK_STREAM, 0);
ma.sin_family = AF_INET;
ma.sin_port = htons(5100);
ma.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sd, (struct sockaddr *) &ma, sizeof(ma)) != -1)
  ...
  1. Connect Example
1
2
3
4
5
6
7
8
9
int sd;
struct sockaddr_in sa;
sd = socket(AF_INET, SOCK_STREAM, 0);
sa.sin_family = AF_INET;
sa.sin_port = htons(5100);
//htons() converts host-byte-order into network-byte-order
sa.sin_addr.s_addr = inet_addr(128.101.34.78);
if (connect(sd, (struct sockaddr *) &sa, sizeof(sa)) != -1)
  ...
  1. Connection-oriented Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int sd, cd, calen;
struct sockaddr_in ma, ca;
sd = socket(AF_INET, SOCK_STREAM, 0);
ma.sin_family = AF_INET;
ma.sin_port = htons(5100);
ma.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sd, (struct sockaddr *) &ma, sizeof(ma));
listen(sd, 5);
calen = sizeof(ca);
cd = accept(sd, (struct sockaddr *) &ca, &calen);
//…read and write to client treating cd as file descriptor…
  1. Client example

Get current time from 192.43.244.18 and print it to stdout.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
 * daytime.c
 *
 * Programmed by G. Adam Stanislav
 */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
  register int s;
  register int bytes;
  struct sockaddr_in sa;
  char buffer[BUFSIZ+1];

  if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    return 1;
  }

  bzero(&sa, sizeof sa);

  sa.sin_family = AF_INET;
  sa.sin_port = htons(13);
  sa.sin_addr.s_addr = htonl((((((192 << 8) | 43) << 8) | 244) << 8) | 18);
  if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
    perror("connect");
    close(s);
    return 2;
  }

  while ((bytes = read(s, buffer, BUFSIZ)) > 0)
    write(1, buffer, bytes);

  close(s);
  return 0;
}
  1. Server example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BACKLOG 4

int main() {
    register int s, c;
    int b;
    struct sockaddr_in sa;
    time_t t;
    struct tm *tm;
    FILE *client;

    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        return 1;
    }

    bzero(&sa, sizeof sa);

    sa.sin_family = AF_INET;
    sa.sin_port   = htons(13);

    if (INADDR_ANY)
        sa.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
        perror("bind");
        return 2;
    }

    switch (fork()) {
        case -1:
            perror("fork");
            return 3;
            break;
        default:
            close(s);
            return 0;
            break;
        case 0:
            break;
    }

    listen(s, BACKLOG);

    for (;;) {
        b = sizeof sa;

        if ((c = accept(s, (struct sockaddr *)&sa, &b)) < 0) {
            perror("daytimed accept");
            return 4;
        }

        if ((client = fdopen(c, "w")) == NULL) {
            perror("daytimed fdopen");
            return 5;
        }

        if ((t = time(NULL)) < 0) {
            perror("daytimed time");

            return 6;
        }

        tm = gmtime(&t);
        fprintf(client, "%.4i-%.2i-%.2iT%.2i:%.2i:%.2iZ\n",
            tm->tm_year + 1900,
            tm->tm_mon + 1,
            tm->tm_mday,
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec);

        fclose(client);
    }
}

Reference

  1. sockets
updatedupdated2022-04-252022-04-25