Network IPC: Sockets
In this chapter, we look at the mechanisms that allow processes running on different computers (connected to a common network) to communicate with one another—network IPC.
Socket Descriptors
A socket is an abstraction of a communication endpoint.
To create a socket, we call the socket function.
#include <sys/socket.h>
int socket(int domain,int type ,int protocol);
Returns: file (socket) descriptor if OK, ?1 on error
The domain argument determines the nature of the communication, including the address format The type argument determines the type of the socket, which further determines the communication characteristics.
/* Protocol families. */ #define PF_UNSPEC 0 /* Unspecified. */ #define PF_LOCAL 1 /* Local to host (pipes and file-domain). */ #define PF_UNIX PF_LOCAL /* POSIX name for PF_LOCAL. */ #define PF_FILE PF_LOCAL /* Another non-standard name for PF_LOCAL. */ #define PF_INET 2 /* IP protocol family. */ #define PF_AX25 3 /* Amateur Radio AX.25. */ #define PF_IPX 4 /* Novell Internet Protocol. */ #define PF_APPLETALK 5 /* Appletalk DDP. */ #define PF_NETROM 6 /* Amateur radio NetROM. */ #define PF_BRIDGE 7 /* Multiprotocol bridge. */ #define PF_ATMPVC 8 /* ATM PVCs. */ #define PF_X25 9 /* Reserved for X.25 project. */ #define PF_INET6 10 /* IP version 6. */ #define PF_ROSE 11 /* Amateur Radio X.25 PLP. */ #define PF_DECnet 12 /* Reserved for DECnet project. */ #define PF_NETBEUI 13 /* Reserved for 802.2LLC project. */ #define PF_SECURITY 14 /* Security callback pseudo AF. */ #define PF_KEY 15 /* PF_KEY key management API. */ #define PF_NETLINK 16 #define PF_ROUTE PF_NETLINK /* Alias to emulate 4.4BSD. */ #define PF_PACKET 17 /* Packet family. */ #define PF_ASH 18 /* Ash. */ #define PF_ECONET 19 /* Acorn Econet. */ #define PF_ATMSVC 20 /* ATM SVCs. */ #define PF_RDS 21 /* RDS sockets. */ #define PF_SNA 22 /* Linux SNA Project */ #define PF_IRDA 23 /* IRDA sockets. */ #define PF_PPPOX 24 /* PPPoX sockets. */ #define PF_WANPIPE 25 /* Wanpipe API sockets. */ #define PF_LLC 26 /* Linux LLC. */ #define PF_CAN 29 /* Controller Area Network. */ #define PF_TIPC 30 /* TIPC sockets. */ #define PF_BLUETOOTH 31 /* Bluetooth sockets. */ #define PF_IUCV 32 /* IUCV sockets. */ #define PF_RXRPC 33 /* RxRPC sockets. */ #define PF_ISDN 34 /* mISDN sockets. */ #define PF_PHONET 35 /* Phonet sockets. */ #define PF_IEEE802154 36 /* IEEE 802.15.4 sockets. */ #define PF_CAIF 37 /* CAIF sockets. */ #define PF_ALG 38 /* Algorithm sockets. */ #define PF_NFC 39 /* NFC sockets. */ #define PF_MAX 40 /* For now.. */ /* Address families. */ #define AF_UNSPEC PF_UNSPEC #define AF_LOCAL PF_LOCAL #define AF_UNIX PF_UNIX #define AF_FILE PF_FILE #define AF_INET PF_INET #define AF_AX25 PF_AX25 #define AF_IPX PF_IPX #define AF_APPLETALK PF_APPLETALK #define AF_NETROM PF_NETROM #define AF_BRIDGE PF_BRIDGE #define AF_ATMPVC PF_ATMPVC #define AF_X25 PF_X25 #define AF_INET6 PF_INET6 #define AF_ROSE PF_ROSE #define AF_DECnet PF_DECnet 。。。。。 省略,类似 #define AF_ALG PF_ALG #define AF_NFC PF_NFC #define AF_MAX PF_MAX
socket type
enum __socket_type { SOCK_STREAM = 1, /* Sequenced, reliable, connection-based byte streams. */ #define SOCK_STREAM SOCK_STREAM SOCK_DGRAM = 2, /* Connectionless, unreliable datagrams of fixed maximum length. */ #define SOCK_DGRAM SOCK_DGRAM SOCK_RAW = 3, /* Raw protocol interface. */ #define SOCK_RAW SOCK_RAW SOCK_RDM = 4, /* Reliably-delivered messages. */ #define SOCK_RDM SOCK_RDM SOCK_SEQPACKET = 5, /* Sequenced, reliable, connection-based, datagrams of fixed maximum length. */ #define SOCK_SEQPACKET SOCK_SEQPACKET SOCK_DCCP = 6, /* Datagram Congestion Control Protocol. */ #define SOCK_DCCP SOCK_DCCP SOCK_PACKET = 10, /* Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level. */ #define SOCK_PACKET SOCK_PACKET /* Flags to be ORed into the type parameter of socket and socketpair and used for the flags parameter of paccept. */ SOCK_CLOEXEC = 02000000, /* Atomically set close-on-exec flag for the new descriptor(s). */ #define SOCK_CLOEXEC SOCK_CLOEXEC SOCK_NONBLOCK = 00004000 /* Atomically mark descriptor(s) as non-blocking. */ #define SOCK_NONBLOCK SOCK_NONBLOCK };
With a datagram ( SOCK_DGRAM)interface, no logical connection needs to exist between peers for them to communicate. All you need to do is send a message addressed to the socket being used by the peer process.
A SOCK_STREAM socket provides a byte-stream service; applications are unaware of message boundaries.
A SOCK_SEQPACKET socket is just like a SOCK_STREAM socket except that we get a message-based service instead of a byte-stream service.
A SOCK_RAW socket provides a datagram interface directly to the underlying network layer (which means IP in the Internet domain).
Calling socket is similar to calling open.In both cases, you get a file descriptor that can be used for I/O. When you are done using the file descriptor,you call close to relinquish access to the file or socket
and free up the file descriptor for reuse.
#include <sys/socket.h> int shutdown(int sockfd ,int how ); Returns: 0 if OK,?1 on error
If how is SHUT_RD,then reading from the socket is disabled. If how is SHUT_WR,then we can’t use the socket for transmitting data. We can use SHUT_RDWR to disable both data transmission and reception.
Given that we can close a socket, why is shutdown needed? There are several reasons. First, close will deallocate the network endpoint only when the last active reference is closed. If we duplicate the
socket (with dup ,for example), the socket won’t be deallocated until we close the last file descriptor referring to it. The shutdown function allows us to deactivate a socket independently of the number of active file descriptors referencing
it. Second, it is sometimes convenient to shut a socket down in one direction only
Addressing
Byte Ordering
If the processor architecture supports big-endian byte order ,then the highest byte address occurs in the least significant byte (LSB ). Little-endianbyte order is the opposite: the least significant byte
contains the lowest byte address. Note that regardless of the byte ordering, the most significant byte (MSB) is always on the left, and the least significant byte is always on the right.
Four functions are provided to convert between the processor byte order and the network byte order for TCP/IP applications.
The h is for ‘‘host’’byte order,and the n is for ‘‘network’’byte order.The l is for ‘‘long’’ (i.e., 4-byte) integer ,and the s is for ‘‘short’ ’( i.e., 2-byte) integer
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostint32 );
Returns: 32-bit integer in network byte order
uint16_t htons(uint16_t hostint16 );
Returns: 16-bit integer in network byte order
uint32_t ntohl(uint32_t netint32);
Returns: 32-bit integer in host byte order
uint16_t ntohs(uint16_t netint16);
Returns: 16-bit integer in host byte order
The h is for ‘‘host’’byte order,and the n is for ‘‘network’’byte order.The l is for ‘‘long’’ (i.e., 4-byte) integer ,and the s is for ‘‘short’ ’( i.e., 2-byte) integer
Address Formats
An address identifies a socket endpoint in a particular communication domain.
linux 下找到的sockaddr的定义:
/* Structure describing a generic socket address. */ struct sockaddr { __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */ char sa_data[14]; /* Address data. */ };
Two new functions — inet_ntop and inet_pton—support similar functionality and work with both IPv4 and IPv6 addresses.
#include <arpa/inet.h> const char *inet_ntop(intdomain,const void *restrict addr , char *restrict str,socklen_t size ); Returns: pointer to address string on success, NULL on error int inet_pton(int domain,const char *restrict str, void *restrict addr ); Returns: 1 on success, 0 if the format is invalid, or ?1 on error
The inet_ntop function converts a binary address in network byte order into a text string; inet_pton converts a text string into a binary address in network byte order.Only two domain values are supported:
AF_INET and AF_INET6
Address Lookup
hostent 结构体的定义:
/* Description of data base entry for a single host. */
struct hostent
{
char *h_name; /* Official name of host. */
char **h_aliases; /* Alias list. */
int h_addrtype; /* Host address type. */
int h_length; /* Length of address. */
char **h_addr_list; /* List of addresses from name server. */
#if defined __USE_MISC || defined __USE_GNU
# define h_addr h_addr_list[0] /* Address, for backward compatibility.*/
#endif
};
#include <netdb.h> struct hostent *gethostent(void); Returns: pointer if OK,NULL on error void sethostent(int stayopen ); void endhostent(void);
If the host database file isn’t already open, gethostent will open it. The gethostent function returns the next entry in the file. The sethostent function will open the file or rewind it if it is already open. When the stay open argument is set to a nonzero value, the file remains open after calling gethostent.The endhostent function can be used to close the file.
各种API要来了。。。
netent:
struct netent { char *n_name; /* Official name of network. */ char **n_aliases; /* Alias list. */ int n_addrtype; /* Net address type. */ uint32_t n_net; /* Network number. */ }; #include <netdb.h> struct netent *getnetbyaddr(uint32_t net,int type ); struct netent *getnetbyname(const char *name); struct netent *getnetent(void); All return: pointer if OK, NULL on error void setnetent(int stayopen ); void endnetent(void);
protoent:
/* Description of data base entry for a single service. */ struct protoent { char *p_name; /* Official protocol name. */ char **p_aliases; /* Alias list. */ int p_proto; /* Protocol number. */ }; #include <netdb.h> struct protoent *getprotobyname(const char *name); struct protoent *getprotobynumber(int proto); struct protoent *getprotoent(void); All return: pointer if OK, NULL on error void setprotoent(intstayopen ); void endprotoent(void);
servent:
/* Description of data base entry for a single service. */ struct servent { char *s_name; /* Official service name. */ char **s_aliases; /* Alias list. */ int s_port; /* Port number. */ char *s_proto; /* Protocol to use. */ }; #include <netdb.h> struct servent *getservbyname(const char *name,const char *proto); struct servent *getservbyport(int port ,const char *proto); struct servent *getservent(void); All return: pointer if OK, NULL on error void setservent(int stayopen ); void endservent(void);
The getaddrinfo function allows us to map a host name and a service name to an address.
/* Structure to contain information about address of a service provider. */ struct addrinfo { int ai_flags; /* Input flags. */ int ai_family; /* Protocol family for socket. */ int ai_socktype; /* Socket type. */ int ai_protocol; /* Protocol for socket. */ socklen_t ai_addrlen; /* Length of socket address. */ struct sockaddr *ai_addr; /* Socket address for socket. */ char *ai_canonname; /* Canonical name for service location. */ struct addrinfo *ai_next; /* Pointer to next in list. */ };
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *restrict host ,const char *restrictservice ,
const struct addrinfo *restrict hint ,struct addrinfo **restrict re s);
Returns: 0 if OK, nonzer oerror code on error
void freeaddrinfo(struct addrinfo *ai );
一个有点小长的demo:
#include <stdio.h> #include <netdb.h> #include <arpa/inet.h> #if defined (BSD) || defined (MACOS) #include <sys/socket.h> #include <netinet/in.h> #endif void print_family(struct addrinfo *aip) { printf("family"); switch(aip->ai_family) { case AF_INET: printf("inet"); break; case AF_INET6: printf("inet6"); break; case AF_UNIX: printf("unix"); break; case AF_UNSPEC: printf("unspecified"); break; default: printf("unknowm"); } } void print_type(struct addrinfo *aip) { printf(" type "); switch (aip->ai_socktype) { case SOCK_STREAM : printf("stream"); break; case SOCK_DGRAM: printf("datagram"); break; case SOCK_SEQPACKET: printf("seqpacket"); break; case SOCK_RAW: printf("raw"); break; default: printf("unknown (%d)",aip->ai_socktype); } } void print_protocol(struct addrinfo *aip) { printf(" protocol "); switch (aip->ai_protocol) { case 0: printf("default"); break; case IPPROTO_TCP: printf("TCP"); break; case IPPROTO_UDP: printf("UDP"); break; case IPPROTO_RAW: printf("RAW"); break; default: printf("unknown (%d)",aip->ai_protocol); } } void print_flags(struct addrinfo* aip) { printf(" flags "); if(aip->ai_flags == 0) { printf(" 0"); } else { if(aip->ai_flags & AI_PASSIVE) { printf("passive"); } if(aip->ai_flags & AI_CANONNAME) { printf(" canon"); } if(aip->ai_flags & AI_NUMERICHOST) { printf(" numhost"); } #if defined (AI_NUMERICSERV) if(aip->ai_flags & AI_NUMERICSERV) { printf(" numserv"); } #endif #if defined (AI_V4MAPPED) if(aip->ai_flags & AI_V4MAPPED) { printf(" v4mapped"); } #endif #if defined (AI_ALL) if(aip->ai_flags & AI_ALL) { printf(" all"); } #endif } } int main(int argc,char* argv[]) { struct addrinfo *aip,*ailist; struct addrinfo hint; struct sockaddr_in *sinp; const char* add; int err; char abuf[INET_ADDRSTRLEN]; if(argc != 3) { printf("usage : %s nodename service\n",argv[0]); return 0; } hint.ai_flags = AI_CANONNAME; hint.ai_family = 0; hint.ai_socktype = 0; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_canonname = NULL; hint.ai_addr = NULL; hint.ai_next = NULL; if((err = getaddrinfo(argv[1],argv[2],&hint,&ailist)) != 0) { printf(" getaddrinfo error\n"); return 0; } for(aip = ailist; aip != NULL;aip = aip->ai_next) { print_flags(aip); print_family(aip); print_type(aip); print_protocol(aip); printf("\n\thost %s",aip->ai_canonname ? aip->ai_canonname:"-"); if(aip->ai_family == AF_INET) { sinp = (struct sockaddr_in*)aip->ai_addr; add = inet_ntop(AF_INET,&sinp->sin_addr,abuf,INET_ADDRSTRLEN); printf(" address %s",add ? add : "unknown"); printf(" port %d",ntohs(sinp->sin_port)); } printf("\n"); } return 0; }
我确实没想到hostname居然是ubuntu
查了一下查看当前hostname的方法是cat /proc/sys/kernel/hostname
于是知道hostname是 ubuntu
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_16$ ./a.out ubuntu nfs
flags canonfamilyinet type stream protocol TCP
host ubuntu address 127.0.1.1 port 2049
flags canonfamilyinet type datagram protocol UDP
host - address 127.0.1.1 port 2049
flags canonfamilyinet type stream protocol TCP
host ubuntu address 127.0.1.1 port 2049
flags canonfamilyinet type datagram protocol UDP
host - address 127.0.1.1 port 2049
Associating Addresses with Sockets
We use the bind function to associate an address with a socket.
There are s everal restrictions on the address we can use:
#include <sys/socket.h> int bind(int sockfd ,const struct sockaddr *addr ,socklen_t len); Returns: 0 if OK,?1 on error
There are s everal restrictions on the address we can use:
?The address we specify must be valid for the machine on which the process is running; we can’t specify an address belonging to some other machine.?The address must match the format supported by the address family we used to create the socket
?The port number in the address cannot be less than 1,024 unless the process has the appropriate privilege (i.e., is the superuser).?Usually ,only one socket endpoint can be bound to a given address, although some protocols allow duplicate bindings.
We can use the getsockname function to discover the address bound to a socket.
#include <sys/socket.h> int getsockname(int sockfd ,struct sockaddr *restrict addr , socklen_t *restrict alenp); Returns: 0 if OK,?1 on error
If the socket is connected to a peer, we can find out the peer’s address by calling the getpeername function.
#include <sys/socket.h> int getpeername(int sockfd ,struct sockaddr *restrict addr , socklen_t *restrict alenp); Returns: 0 if OK,?1 on error
Connection Establishment
We use the connectfunction to create a connection.
If sockfd is not bound to an address, connect will bind a default address for the caller
#include <sys/socket.h> int connect(intsockfd ,const struct sockaddr *addr ,socklen_t len); Returns: 0 if OK,?1 on error
If sockfd is not bound to an address, connect will bind a default address for the caller
A server announces that it is willing to accept connect requests by calling the listen function.
Once a server has called listen,t he socket used can receive connect requests. We use the accept function to retrieve a connect request and convert it into a connection.
#include <sys/socket.h> int listen(int sockfd ,int backlog ); Returns: 0 if OK,?1 on error
Once a server has called listen,t he socket used can receive connect requests. We use the accept function to retrieve a connect request and convert it into a connection.
#include <sys/socket.h> int accept(int sockfd ,struct sockaddr *restrict addr , socklen_t *restrict len); Returns: file (socket) descriptor if OK, ?1 on error
我想这章。。。必须搁置在这里了。。。网络API Richard专门写了本书。。。这章的内容感觉根本架不起一个框架来。学会放下,之后再拿起来!
《APUE》chapter 16 Network IPC: Sockets 学习笔记(加上自己的代码),布布扣,bubuko.com