Sockets Programming Quick Reference

Overview

See the https://beej.us/guide/bgnet/ or the man pages for more detailed descriptions of the functions

Client Programming: socket(), getaddrinfo/inet_aton/inet_addr/inet_pton, connect(), recv()/send() Server Programming: socket(), getaddrinfo/inet_aton/inet_addr/inet_pton, bind(), listen(), accept(), recv()/send()

socket()

  • int socket(int domain, int type, int protocol);
  • The domain specifies what type of socket we want---for this lecture, it will be one of PF_INET or PF_INET6
  • The type for this lecture will always be SOCK_STREAM (meaning TCP, it could also be SOCK_DGRAM for UDP)
  • The protocol is the protocol number (e.g., one of IPPROTO_TCP or IPPROTO_UDP, but we can use 0 since SOCK_STREAM means TCP, and it will figure it out)
  • Returns a "file descriptor" on success and <0 on error (setting errno as appropriate)

struct sockaddr

  • struct sockaddr is the generic type for a socket address, but we'll use struct sockaddr_in or struct sockaddr_in6 and cast to a struct sockaddr
struct sockaddr {
    unsigned short sa_family; // address family, AF_xxx
    char sa_data[14]; // 14 bytes of protocol address
};
struct sockaddr_in {
    short int sin_family; // Address family, AF_INET
    unsigned short int sin_port; // Port number
    struct in_addr sin_addr; // Internet address
    unsigned char sin_zero[8]; // Same size as struct sockaddr
};
struct in_addr {
    uint32_t s_addr; // that's a 32-bit int (4 bytes)
};
struct sockaddr_in6 {
    u_int16_t sin6_family; // address family, AF_INET6
    u_int16_t sin6_port; // port number, Network Byte Order
    u_int32_t sin6_flowinfo; // IPv6 flow information
    struct in6_addr sin6_addr; // IPv6 address
    u_int32_t sin6_scope_id; // Scope ID
};
struct in6_addr {
    unsigned char s6_addr[16]; // IPv6 address
};

inet_pton(), inet_addr(), and inet_aton()

  • aton and addr only work for IPv4 addresses
  • int inet_aton(const char *cp, struct in_addr *inp);
  • in_addr_t inet_addr(const char *cp);
  • cp is a string of a dotted quad IP address
  • int inet_pton(int af, const char *src, void *dst);

getaddrinfo()

int getaddrinfo(const char *node, // e.g. "www.example.com" or IP
                const char *service, // e.g. "http" or port number
                const struct addrinfo *hints,
                struct addrinfo **res);
  • Gives us a linked list of struct addrinfos
struct addrinfo {
    int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc.
    int ai_family; // AF_INET, AF_INET6, AF_UNSPEC
    int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
    int ai_protocol; // use 0 for "any"
    size_t ai_addrlen; // size of ai_addr in bytes
    struct sockaddr *ai_addr; // struct sockaddr_in or _in6
    char *ai_canonname; // full canonical hostname
    struct addrinfo *ai_next; // linked list, next node
};

bind()

  • int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
  • Binds our socket to the address and port specified by my_addr
  • We will often use INADDR_ANY f to indicate that we want to accept any IPv4 connection (slightly different for IPv6, see “Jumping from IPv4 to IPv6” on Beej’s guide)

Also, copied from Beej's guide, if you have an error that the address is already in use, you can fix that as follows

int yes = 1;
// lose the pesky "Address already in use" error message
if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1) {
    perror("setsockopt");
    exit(1);
}

listen()

  • int listen(int sockfd, int backlog);
  • Starts our socket "listening" (what a server would do)
  • backlog is how many outstanding requests can be queued until we accept them

accept()

  • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • Returns a file descriptor for a remote connection
  • We'll use a struct sockaddr_storage (guaranteed large enough to store any address) for the address
struct sockaddr_storage {
    sa_family_t ss_family; // address family
    // all this is padding, implementation specific, ignore it:
    char __ss_pad1[_SS_PAD1SIZE];
    int64_t __ss_align;
    char __ss_pad2[_SS_PAD2SIZE];
};

connect()

  • int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
  • Useful for the client, connects our local socket to the remote address

send()

  • int send(int sockfd, const void *msg, int len, int flags);
  • Returns how many bytes were actually sent (may be less than we requested, which we'll have to handle)
  • flags can be 0 by default
  • Note that while we could use write, we tend to use send instead since it lets us to more specific socket things (see the man page for flags)

recv()

  • int recv(int sockfd, void *buf, int len, int flags);
  • Returns how many bytes were received (no more than len)
  • Returns <0 on error, 0 when remote side has closed

close()

  • close(sockfd);
  • Prevents any further reads or writes to the socket, the remote peer will receive an error on trying to read or write
  • Also, marks the fd as usable again (no longer counts toward our per-process limit)

shutdown()

  • int shutdown(int sockfd, int how);
  • Note that you will still have to close eventually
how Effect
0 Further receives are disallowed
1 Further sends are disallowed
2 Further sends and receives are disallowed (like close())