Sockets are a way to speak to other programs using standard Unix file descriptors.
socket() -> connect() -> [send()|recv()]
int socket(int _domain, int _type, int _protocol) _THROW;
- Creat a socket.
- make a call to
socket()system routine, specifying type, protocol and domain socket()returns a socket descriptor
- Connect to a remote address
- specify IP and port
- Communicate through it.
- use specialized
send()andrecv()socket calls
Some Types of sockets
- Internet sockets - DARPA internet addresses; 2 types:
- Stream sockets
SOCK_STREAM - Datagram sockets
SOCK_DGRAM, sometimes called connectionless sockets
- Stream sockets
- Unix sockets - path names on local nodes
- X.25 sockets - CCITT X.25 addresses
- Raw sockets - allow access to underlying transport provider (very powerful)
SOCK_RAW
The client socket is created with socket(), connected to a remote address with connect() and finally retrieves data with recv().
socket() -> connect() -> recv()
The server socket is created with socket(), bound to an IP port with bind(), monitors that port with listen(), allows an in-bound connection to a client socket with accept() and finally communicates two-way with send() and recv().
socket() -> bind() -> listen() -> accept()
provide errorless, in-order delivery via 2-way communication.
- telnet and HTTP use stream sockets
- use TCP to achieve this
provide out-of-order, unreliable delivery
- use UDP (User Datagram Protocol) to achieve this
- connectionless; don't have to maintain open connection, i.e., low overhead, quick communication
- tftp, dhcpcd (DHCP client), multiplayer games, streaming audio and video use datagram sockets
- reliable
SOCK_DGRAMapplications use an acknowledgement procedure which confirms delivery of each datagram packet by getting anACKfrom the receiver, otherwise resending those notACK-ed
127.0.0.1 v4 loopback address
::1 v6 loopback address
192.0.2.33 (v4) --> ::ffff:192.0.2.33
255.255.255.252 -> 11111111 . 11111111 . 11111100
- also referred to by
/30representing the number of bits for the network portion - only last 2 bits get through, representing possible host addresses
- the rest are for network addresses
Big-Endian - aka, network byte order, the bigger numbers are on the left, as usual; b34f is stored in sequential bytes as b3 and then 4f
Little-Endian - bytes are stored in reverse order; b34f is stored sequentially as 4f, then b3
- you will always run values through a function to convert host byte order (which might be big-endian or little-endian) into big-endian/network byte order before sending them through a socket connection
Byte-order Functions Table
| Byte-Order Function | Description |
|---|---|
htons() |
host-to-network short |
htonl() |
host-to-network long |
ntohs() |
network-to-host short |
ntohl() |
network-to-host long |
- For floating point numbers, check out serialization
a struct called addrinfo is used to prep socket address structures for subsequent use, and for host name and service name lookups
addrinfo struct layout
struct addrinfo {
int ai_flags; // AI_PASSIVE, AI_CANNONNAME, 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
}- load struct
- call
getaddrinfo(), which returns ptr to new linked list of the above structs populated with the needed info.
- use
ai_familyto force selection of IPv4 (AF_INET) or v6 (AF_INET6), or useAF_UNSPEC
The embedded structs inside the above might need to be accessed to get info. from them. They're structured as follows:
sockaddr struct
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};-
sa_familyfor our purposes will be eitherAF_INETorAF_INET6 -
sa_datacontains destination address and port # for the socket -
To deal w/
struct sockaddr, we usestruct sockaddr_infor use with IPv4- a ptr to struct sockaddr_in can be cast to a ptr to
struct sockaddrand vice versa - so, even though
connect()wantsstruct sockaddr*, you can usestruct sockaddr_inby casting it to the former at the last minute
- a ptr to struct sockaddr_in can be cast to a ptr to
sockaddr_in struct
// IPv4-only, see struct sockaddr_in6 for IPv6
struct sockaddr_in {
shrot 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
};- this struct makes it easy to ref elements of socket address
sin_zeroshould be set to all zeros viamemset()sin_familycorresponds tosa_familyand should be set toAF_INETsin_portmust be in Network Byte Order by usinghtons()
Digging deeper, here's struct in_addr from above:
in_addr struct
// IPv4-only - see struct in6_addr for IPv6
// Internet address (a structure for historical reasons
struct in_addr {
uint32_t s_addr; // a 32-bit int (4 bytes)
};- used to be a union, but now it's an
int
IPv6 Setup Example
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
};
// not knowing if struct will be filled out w/IPv4 or v6, this parallel struct can handle either, then be cast to the type needed
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];
};
- IP address must first be converted via
inet_pton(), then stored - pton stands for 'presentation to network'
- the code below is not robust - always check that it worked and handle it if it fails (returns -1)
Storing IP Address in Struct Example
struct sockaddr_in sa; // IPv4
struct sockaddr_in6 sa6; // IPv6
inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6
- use
ntop()to convert from struct's binary representation to the dot-notation presentation format - ntop stands for network to presentation
Retrieving IP Address from Struct Example
// IPv4
char ip4[INET_ADDRSTRLEN]; // space to hold IPv4 string
struct sockaddr_in sa;
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s\n", ip4);
// IPv6:
char ip6[INET_ADDRSTRLEN]; // space to hold the UPv6 string
struct sockaddr_in6 sa6;
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s\n", ip6);- these functions don't work with domain names, only IP addresses; working with domain names requires the use of
getaddrinfo()
- Try using
getaddrinfo()to get all thestruct sockaddrinfo instead of packing the structs by hand. Keeps you IP version-agnostic & eliminates many of the next steps. - Anywhere you're hard-coding anything related to IP version, try wrapping it up in a helper function.
- Change
AF_INETtoAF_INET6 - Change
PF_INETtoPF_INET6 - Change
INADDR_ANYtoin6addr_anywhich are slightly different:
IPv4 -> v6 Example
struct sockaddr_in sa;
struct sockaddr_in6 sa6;
sa.sin_addr.s_addr = INADDR_ANY; // use my IPv4 address
sa6.sin6_addr = in6addr_any; // use my IPv6 address- Also, the value IN6ADDR_ANY_INIT can be used as an initializer when the struct in6_addr is
declared, like so:
struct in6_addr ia6 = IN6ADDR_ANY_INIT;
- Instead of
struct sockaddr_inusestruct sockaddr_in6, being sure to add “6” to the fields as appropriate (see structs, above). There is no sin6_zero field. - Instead of
struct in_addrusestruct in6_addr, being sure to add “6” to the fields as appropriate (see structs, above). - Instead of
inet_aton()orinet_addr(), useinet_pton(). - Instead of
inet_ntoa(), useinet_ntop(). - Instead of
gethostbyname(), use the superiorgetaddrinfo(). - Instead of
gethostbyaddr(), use the superiorgetnameinfo()(althoughgethostbyaddr()can still work with IPv6). INADDR_BROADCASTno longer works. Use IPv6 multicast instead.
- the order of calls is important and trips people up; read the
man pages - the following is in order but missing error-checking code for brevity
- sets up structs needed later
- returns pointer to linked-list,
res, of results - give it 3 params
nodeis host name or IP address to connect toserviceis port # or name of particular service like ftp, http, etc. (see IANA Port List or /etc/services)hintspoints to the structaddrinfoyou've already filled w/relevant info.
getaddrinfo() Example
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
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); Here's a sample call on a server that wants to listen to its own port 3490; note it doesn't do any listening, just sets up structures needed to do so:
Server Struct Setup
1 int status;
2 struct addrinfo hints;
3 struct addrinfo *servinfo; // will point to the results
4
5 memset(&hints, 0, sizeof hints); // make sure the struct is empty
6 hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
7 hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
8 hints.ai_flags = AI_PASSIVE; // fill in my IP for me
9
10 if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
11 fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
12 exit(1);
13 }
14
15 // servinfo now points to a linked list of 1 or more struct addrinfos
16
17 // ... do everything until you don't need servinfo anymore ....
18
19 freeaddrinfo(servinfo); // free the linked-listAI_PASSIVEavoids having to hard-code an IP address; assigns address of localhost to the socket structures; could also have put a specific address in as first param togetaddrinfo(), currently set toNULLabove
Here is a simple call if you're client wanting to connect to particular server (www.example.net:3490); this doesn't connect, just sets up structures used later:
Client Connecting to Server Example
int status;
struct addrinfo hints;
struct addrinfo *servinfo; // will point to results
memset(&hints, 0, sizeof hints); // make sure struct is empty
hints.ai_family = AF_UNSPEC; // don't care if IPv4 or v6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
// get ready to connect
status = getaddrinfo("www.example.net", "3490", &hints, &servinfo);
// servinfo now points to a linked list of 1 or more struct addrinfos
// etc.Short Socket Program Example
/*
** showip.c -- show IP addresses for a host given on command line
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr, "usage: showip hostname\n");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET66 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return(2);
}
printf("IP addresses for %s:\n\n", argv[1]);
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
freeaddrinfo(res); // free the linked list
return 0;
}int s;
struct addrinfo hints, *res;
// do lookup
// having already filled out "hints" struct
getaddrinfo("www.example.com", "http", &hints, &res);
// should do error-checking on getaddrinfo() and walk
// "res" linked list looking for valid entries instead of
// assuming 1st one is good
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);- commonly done if you'll
Listen()for incoming connections on a specific port - if you're only doing a
connect()as the client and not the server, this is likely unnecessary
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);-sockfd is socket file descriptor returned by socket()
my_addris pointer to structsockaddrthat contains info. about your address, namely port and IP addressaddrlenis length in bytes of that address
Binding Port Example
struct addrinfo hints, *res;
int sockfd;
// first, load up address structs with getaddrinfo():
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints_ai_flags = AI_PASSIVE; // fill in IP for me
getaddrinfo(NULL, "3490", &hints, &res);
// make socket:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
// bind it to port we passed in to getadrinfo()
bind(sockfd, res->ai_addr, res->ai_addrlen);Binding Port the OLD WAY Example
// The OLD Way
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr);- could also assign
INADDR_ANYto s_Addr field if desired, to bind to your local IP address - ports below 1024 are reserved, use anything between 1025 and 65535 which isn't already being used by another program
- sometimes you will rerun a server and
bind()fails, saying 'Address already in use', can wait until it's clear (minute or so) or add code allowing reuse of port:
int yes=1;
// char yes='1'; // Solaris people use this
// lose the pesky "Address already in use" error message
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
perror("setsockopt");
exit(1);
}- sometimes you won't have to call
bind(), e.g., whenconnect()ing to remote machine and don't care what local port is (telnet) - can callconnect()which will check to see if socket is unbound and willbind()it to an unused local port if necessary
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);struct addrinfo hints, *res;
int sockfd;
// first, load up address structs w/getaddrinfo():
memset(&hints, 0, sizeof hints);
hints_ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("www.example.com", "3490", &hints, &res);
// make a socket:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
// connect!
connect(sockfd, res->ai_addr, res->ai_addrlen);- be sure to check return value from
connect()which will return -1 on error and set variableerrno
- listen for incoming connections instead of connecting to remote host
- 2-step process:
listen()andaccept()
int listen(int sockfd, int backlog);
-sockfd - socket file descriptor returned from socket() call @ socket creation
-
backlog- # connections allowed on incoming queue, often set to 20, 5 - 10 is probably ok -
listen()returns -1 and setserrnoon error
getaddrinfo();socket();bind();listen();accept();
Process:
- You are listening on a bound port.
- Someone tries to connect, getting into the queue.
- You accept their connection, telling them to get the pending connection.
- You get returned a new socket (file descriptor) for this single connection. You now have two sockets - one for listening and the other for the actual client connection and communication.
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);sockfd()-listen()ing socket (descriptor)addr- usually a pointer to local structsockaddr_storage, where info. about incoming connection will go, which tells what host is calling from which portaddrlen- local integer variable that should be set tosizeof(struct sockaddr_storage)before its address is passed toaccept()-accept()won't put more than that many bytes intoaddrand if it puts fewer,addrlenwill be changed to reflectaccept()returns -1 and setserrnoif there's an error
`accept()` Example
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#define MYPORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
int main(void)
{
struct sockaddr_storage their_addr;
socklen_t addr_size;
struct addrinfo hints, *res;
int sockfd, new_fd;
// !! don't forget your error checking for these calls !!
// first, load up address structs with getaddrinfo():
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
getaddrinfo(NULL, MYPORT, &hints, &res);
// make a socket, bind it, and listen on it:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(sockfd, res->ai_addr, res->ai_addrlen);
listen(sockfd, BACKLOG);
// now accept an incoming connection:
addr_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
// ready to communicate on socket descriptor new_fd!
.
.
.- use socket descriptor
new_fdfor allsend()andrecv()calls - you can close the listening
sockfd
- for communicating over stream or connected datagram sockets
- for communicating over unconnected datagram sockets, use
sendto()andrecvfrom()instead
int send(int sockfd, const void *msg, int len, int flags);
sockfd- socket descriptor you want to send data to (returned bysocket()oraccept()msg- pointer to data you want sentlen- length of data (msg) in bytesflags- set to zero (seesend()man page for more info.)
char *msg = "Beej was here!";
int len, bytes_sent;
.
.
.
len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);
.
.
.send()returns actual # of bytes sent, possibly less than what you sent; it's up to you to send the rest- if package is less than 1K, it will likely send the whole thing at once
- -1 returned on error and
errnois set to error number
int recv(int sockfd, void *buf, int len, int flags);
recv()returns # bytes actually read into buffer or -1 w/errnosetrecv()can return 0 - means remote side closed connection on you
- for unconnected Datagram sockets
- requires destination address
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, socklen_t tolen);
-
similar to above stream socket call to
send(), but w/2 addtl. pieces of info. -
to- pointer to structsockaddr, likely asockaddr_in,sockaddr_in6 orsockaddr_storage struct, the latter of which being cast at the last minute- whichever it is, it contains dest. IP and port
-
tolen- an int, can be set simply tosizeof *toorsizeof(struct sockaddr_storage) -
sendto()returns # of bytes actually sent (might be less than # told to send), or -1 on error -
to get dest. addr. info., you'll likely use
getaddrinfo()orrecvfrom(), or fill it out by hand
int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
from- pointer to local structsockaddr_storage, which will be filled w/IP address and port of originating machinefromlen- pointer to local int that should be initialized tosizeof *fromorsizeof(struct sockaddr_storage), returns length of address actually stored infromrecvfrom()returns # of bytes received or -1 on error (w/errnoset accordingly)
-
use
close(sockfd)to prevent any more reads or writes; simple method -
for more control, use
shutdown()- allows cutting off communication in a certain direction or both ways
int shutdown(int sockfd, int how);
- allows cutting off communication in a certain direction or both ways
| how | Effect |
|---|---|
| 0 | Further receives are disallowed |
| 1 | Further sends are disallowed |
| 2 | Further sends and receives are disallowed (like chose()) |
shutdown()doesn't actually close file descriptor, just changes its usability; to free a socket descriptor, useclose()- if using Windows, use
closesocket()instead ofclose()
tells you who is at other end of a connected stream socket
#include <sys/socket.h>
`int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);`-
sockfdis descriptor of connected stream socket,addris pointer to structsockaddr(or structsockaddr_in) which will hold info. about other side of connection -
addrlenis pointer to an int that should be initialized tosizeof *addrorsizeof(struct sockaddr) -
returns -1 on error and sets
errnoaccordingly -
once you have their address, can use
inet_ntop(),getnameinfo()orgethostbyaddr()to print or get more info.
- returns name of computer your program is running on, which can then be used by
gethostbyname()to determine IP address of local machine
#include <unistd.h>
int gethostname(char *hostname, size_t size);hostname- pointer to an array of chars that will contain hostname upon function's returnsize- length in bytes of hostname array
- client server interaction a la Firefox/Apache or ftp/ftpd
- process: server waits for connection,
accept()s one,fork()s a child process to handle it
Stream Server Example
/*
** server.c -- a stream socket server demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3490" // port users connect to
#define BACKLOG 10 // // how many pending connections queue will hold
void sigchld_handler(int s)
{
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while (waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
// get sockaddr, Iv4 or v6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
// clear unused socket from previously-closed connection to reuse port
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo); // all done with this structure
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while (1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (send(new_fd, "Hello, world!\n", 13, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}- start server, then run client in a different terminal tab
Stream Client Example
/*
** client.c -- a stream socket client demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define PORT "3490" // port client will be connecting to
#define MAXDATASIZE 100 // max # bytes we can get at once
// get sockaddr, IPv4 or v6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr, "usage: client hostname\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv == getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all results and connect to first we can
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo); // all done w/this structure
if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("client: received '%s'\n",buf);
close(sockfd);
return 0;
}