《APUE》chapter 16 Network IPC: Sockets 学习笔记(加上自己的代码)

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.
#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




Associating Addresses  with  Sockets



We  use the bind function to associate an address with a socket.
#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.
#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.
#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 学习笔记(加上自己的代码),古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。