Network IPC: socket, network ipc
Inter-process network communication: socket API Introduction
Processes running on different computers (connected through the network) communicate with each other ).
A process can be uniquely identified by a process PID locally, but this does not work in the network. In fact, the TCP/IP protocol family has helped us solve this problem.IP address"Can uniquely identify the host in the network, while the transport layer"Protocol + PortCan uniquely identify the application (process) in the host ). In this way, triples (IP address, protocol, Port) To form a socket, you can identify the network process, and process Communication in the network can use this flag to interact with other processes.
Socket is the abstraction of the communication port! Through the Socket network IPC interface, the process can use this interface to communicate with other processes.
Several definitions:
Socket Descriptor
Socket is the abstraction of the endpoint. The socket descriptor is also required for access to the socket, just as the file descriptor is used by the application process to access the file. Socket descriptors are implemented using file descriptors in UNIX systems.
To create a socket, you can call the socket function.
#include<sys/socket.h>int socket(int domain, int type, int protocol);
Parameters:
Purpose: socket () is used to create a socket descriptor, which uniquely identifies a socket.
Network byte order
The Network Protocol specifies the byte sequence. Therefore, heterogeneous computer systems can exchange protocol information without obfuscation of the byte sequence. The TCP/IP protocol stack adopts the large-end byte sequence. When the application process exchanges and formatting data, the bytecode problem occurs. For TCP/IP, IP addresses are expressed in the network byte order. Therefore, application processes sometimes need to convert between the processor's byte order and the network's byte order.
#include<arpa/inet.h>uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort);uint32_t ntohl(uint32_t netlong);uint16_t ntohs(uint16_t netshort);
These function names are easy to remember. h Represents host, n represents network, l represents 32-bit long integers, and s represents 16-bit short integers.
When you bind an address to a socket, first convert the host's byte order to the network's byte order. Do not make any assumptions about the host's byte order, be sure to convert it to the byte sequence of the network and then assign it to the socket!
Bind a socket to an address
The address associated with the client socket is of little significance, so that the system can select a default address. However, for servers, you need to bind a well-known address to a socket that receives client requests. The client should have a method to connect to the server address. The simplest method is to keep an address for the server and register it in/etc/services or a name service.
You can use the bind function to solve this problem:
#include <sys/types.h> #include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Parameters:
The first parameter: The bind () function assigns a specific address in an address family to the sockfd (socket description ). For example, AF_INET and AF_INET6 assign an ipv4 address or ipv6 address and port number to the socket.
The second parameter: struct sockaddr * pointer, pointing to the Protocol address to be bound to sockfd. The address structure varies depending on the address protocol family when the socket is created:
Address formatThe address identifies the socket endpoint in a specific communication domain. The address format is related to a specific communication domain.To enable addresses of different formats to be passed in to the socket function, the addresses must be strongly converted to the general address structure sockaddr.
// Header file # include <netinet/in. h>
Struct sockaddr is a common address structure defined as follows:
struct sockaddr{ sa_family_t sa_family; char sa_data[14];}
IPV4 internet domain:
// Ipv4 corresponds to/* network address */struct in_addr {uint32_t s_addr;/* address in network byte order */}; struct sockaddr_in {sa_family_t sin_family; /* address family: AF_INET */in_port_t sin_port;/* port in network byte order */struct in_addr sin_addr;/* internet address */};
IPv6 internet domain:
// Ipv6 corresponds to struct in6_addr {unsigned char s6_addr [16];/* IPv6 address */}; struct sockaddr_in6 {sa_family_t sin6_family;/* AF_INET6 */in_port_t sin6_port; /* port number */uint32_t sin6_flowinfo;/* IPv6 flow information */struct in6_addr sin6_addr;/* IPv6 address */uint32_t sin6_scope_id;/* Scope ID (new in 2.4) */};
The Unix domain corresponds:
#define UNIX_PATH_MAX 108struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[UNIX_PATH_MAX]; /* pathname */ };
The third parameter: addrlen corresponds to the address length.
Return Value: 0 is returned for success, and-1 is returned for Error
Function: Bind the socket to the port number, that is, assign a combination of IP addresses and port numbers to the socket
Conversion between Point-to-decimal IP address and network byte IP Address
Sometimes it is necessary to print the address format that can be understood by people rather than computers. We can use functions to convert the binary address format and the dot-decimal format. However, these functions only support IPv4 addresses.
# Include <sys/socket. h> # include <netinet/in. h> # include <arpa/inet. h> // Point-to-decimal IP address translation network byte order IP int inet_aton (const char * cp, struct in_addr * indium ); // Point-to-decimal IP address translation network byte order IP in_addr_t inet_addr (const char * cp); // network byte order IP address conversion point-to-decimal IP char * inet_ntoa (struct in_addr in );
Inet_ton and inet_ntop can not only convert the in_addr of IPv4, but also the in6_addr of IPv6. Therefore, the function interface is of the void * type!
# Include <arpa/inet. h> // IP address translation in byte sequence in the network is divided into decimal IPconst char * inet_ntop (int af, const void * src, char * dst, socklen_t size ); // IPint inet_ton (int af, const char * src, void * dst );
Listeners
As a server, after socket () and bind () are called, listen () is called to listen to the socket. If the client calls connect () to send a connection request, the server receives the request.
The server calls listen to receive connection requests!
#include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog);
Parameters: Sockfd is the description of the socket to be listened on, and backlog is the maximum number of connections that can be queued for the corresponding socket
Return Value: 0 is returned for success, and-1 is returned for Error
Function: When the socket function creates a socket, it is an active socket by default. The listen function converts an unconnected socket that has not called connect into a passive socket, indicates that the kernel should receive connection requests directed to the socket. (Active/customer-> passive/Server)
Connection
For connection-oriented network services, a connection must be established between the process socket (client) requesting the service and the process socket (server) providing the service before data exchange, use the connect function:
#include <sys/types.h> #include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Parameters: The first parameter sockfd is the socket description of the client, the second parameter is the socket address of the server, and the third parameter is the length of the socket address.
Return Value: 0 is returned for success, and-1 is returned for Error
Function: The client establishes a connection with the TCP server by calling the connect function.
Note: the address specified in connect is the server address for communication. If sockfd is not bound to an address, connect will bind a default address to the caller!
Use the accept function to obtain the connection request and establish a connection
#include <sys/types.h> #include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
Parameter: The first parameter is the socket description of the server, the second parameter is the pointer to struct sockaddr *, used to return the Protocol address of the client, and the third parameter is the length of the Protocol address
Return Value: If accpet succeeds, the returned value is a brand new descriptive word automatically generated by the kernel. the descriptor is connected to the client that calls connect. The new socket descriptor has the same socket type and address family as the original socket descriptor.
Note: The original socket sent to accept is not associated with this connection, but is still available and accept other connection requests!
In general, the first parameter of accept is the socket description of the server, which is generated by the server starting to call the socket () function; the accept function returns the connected socket description. A server generally only creates one listening socket description, which exists throughout the lifecycle of the server. The kernel creates a connection socket description for each client connection accepted by the server process. When the server completes the service to a customer, the corresponding connected socket description is closed.
Data Transmission
Since the socket endpoint represents the file descriptor, you can use write and read to communicate through the socket as long as a connection is established.
#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);ssize_t read(int fd, void *buf, size_t count);
Write ()Write the memory referred to by the pointer buf into the file referred to by the fd parameter (the file read/write location will also move). If the write () operation succeeds () returns the actual number of bytes written. -1 is returned when an error occurs. The error code is stored in errno!
Read ()The file referred to by the parameter fd is transferred to the memory referred to by the buf pointer in bytes of nbyte, and the number of bytes read is returned successfully. If an error is returned,-1 is returned and errno is set, if the end of the file has been reached before the read operation is called, 0 is returned for this read operation.
If you want to specify multiple options, receive data packets from multiple clients, or send out-of-band data, you must use one of the six socket functions that transmit data.
Three functions are used to send data:
#include <sys/types.h>#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
Three functions are used to receive data:
#include <sys/types.h>#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
Disable socket Descriptor
The close function is used to close the file descriptor:
#include <unistd.h>int close(int fd);
Note: The close operation only counts the reference count of the corresponding socket description-1. Only when the reference count is 0 will the TCP client send a request to terminate the connection to the server.
Address "reuse"
By default, a socket cannot be bound with a local address in use. But sometimes it is necessary to "reuse" the address. Because each connection is uniquely determined by the combination of the local address and the remote address, as long as the remote address is different, binding two sets of interfaces with one address is not a major problem. To implement the notification set interface, do not bind an address to another set interface because it has been used by one set interface. The application can set the SO_REUSEADDR option before bind () is called. Note that this option is interpreted only when bind () is called. Therefore, you do not need to (but are harmless) set this option for a socket that does not share an address, or in bind () set or clear this option without affecting this or other sets of interfaces.
To solve this problem, use setsockopt () to set SO_REUSEADDR to 1, which indicates that multiple socket descriptors with the same port number but different IP addresses are allowed to be created. Insert the following code between the socket () and bind () calls of the server code:
int opt=1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
Basic socket communication process:
Sample Code
Create a TCP-based socket API
Server:
/*************************************** * *********************************> File Name: server. c> Author: Lynn-Zhang> Mail: iynu17@yeah.net> Created Time: fri 29 Jul 2016 12:15:28 pm cst ********************************* ***************************************/ # include <stdio. h> # include <netinet/in. h> # include <sys/types. h> # include <sys/socket. h> # include <string. h> # include <stdlib. h> # include <arpa/inet. h> # Include <pthread. h> static void usage (const char * proc) {printf ("Usage: % s [ip] [port] \ n", proc );} void * thread_run (void * arg) {printf ("create a new thread \ n"); int fd = (int) arg; char buf [1024]; while (1) {// The server reads data from the socket descriptor to the buf and prints the data. Then, it writes its reply to the socket descriptor memset (buf, '\ 0', sizeof (buf )); ssize_t _ s = read (fd, buf, sizeof (buf)-1); if (_ s> 0) {buf [_ s] = '\ 0 '; printf ("client: # % s", buf); printf ("server: $"); fflush (stdout); // The server writes the reply to fd memset (buf, '\ 0', sizeof (buf); ssize_t _ in = read (0, buf, sizeof (buf) -1); if (_ in> = 0) {buf [_ in-1] = '\ 0'; write (fd, buf, strlen (buf ));} printf ("please wait... \ n ");} else if (_ s = 0) {printf (" client close... \ n "); break;} else {printf (" read error... \ n "); break;} return (void *) 0;} int main (int argc, char * argv []) {// The parameter must constitute the complete socket if (argc! = 3) {usage (argv [0]); exit (1) ;}// create a server socket int listen_sock = socket (AF_INET, SOCK_STREAM, 0 ); if (listen_sock <0) {perror ("socket"); return 1;} struct sockaddr_in local; local. sin_family = AF_INET; local. sin_port = htons (atoi (argv [2]); local. sin_addr.s_addr = inet_addr (argv [1]); int opt = 1; if (setsockopt (listen_sock, SOL_SOCKET, SO_REUSEADDR, & opt, sizeof (opt) <0) {perror ("setsockopet error \ n"); return-1;} // Bind the socket to the server IP address and port number to bind if (bind (listen_sock, (struct sockaddr *) & local, sizeof (local) <0) {perror ("bind"); return 2;} // create a listening queue and wait for the socket connection request listen (listen_sock, 5); struct sockaddr_in peer; socklen_t len = sizeof (peer); while (1) {// get the connection request and establish the connection int client_sock = accept (listen_sock, (struct sockaddr *) & peer, & len ); if (client_sock <0) {perror ("accept faild... \ n "); return 3;} printf (" get a new link, socket -> % S: % d \ n ", inet_ntoa (peer. sin_addr); pthread_t id; pthread_create (& id, NULL, thread_run, (void *) client_sock); pthread_detach (id); // pid_t id = fork (); // if (id = 0) // {// child // char buf [1024]; // while (1) /// {//// read the data in the specified file description of the listening subfolder to buf. // memset (buf, '\ 0', sizeof (buf )); // ssize_t _ s = read (client_sock, buf, sizeof (buf)-1); // if (_ s> 0) // {// buf [_ s-1] = '\ 0' // printf ("client: # % s \ n", buf); // printf ("server: $ "); // Fflush (stdout); // memset (buf, '\ 0', sizeof (buf); // ssize_t _ s = read (0, buf, sizeof (buf)-1); // if (_ s> 0) // {// buf [_ s-1] = '\ 0'; // write (client_sock, buf, strlen (buf); //} // else // {// printf ("Fail! \ N "); // else // {// printf (" read done... \ n "); // break; //} // else // {// father // waitpid (-1, NULL, WNOHANG); //} close (listen_sock); return 0 ;}
Client:
/*************************************** * *********************************> File Name: client. c> Author: Lynn-Zhang> Mail: iynu17@yeah.net> Created Time: fri 29 Jul 2016 09:00:01 am cst ********************************* ***************************************/ # include <stdio. h> # include <netinet/in. h> # include <sys/types. h> # include <sys/socket. h> # include <string. h> # include <stdlib. h> # include <arpa/inet. h> # Include <errno. h> # include <pthread. h> static usage (const char * proc) {printf ("Usage: % s [ip] [port] \ n", proc);} int main (int argc, char * argv []) {// The input parameter is a complete socket (IP address + port number) if (argc! = 3) {usage (argv [0]); exit (1) ;}// create a socket descriptor int sock = socket (AF_INET, SOCK_STREAM, 0 ); if (sock <0) {perror ("socket"); return 2;} // In An IPv4 internet domain (AF_INET), the socket address uses sockaddr_in to represent struct sockaddr_in remote; remote. sin_family = AF_INET; // socket communication domain remote. sin_port = htons (atoi (argv [2]); // port number remote. sin_addr.s_addr = inet_addr (argv [1]); // ip address // connection request int ret = connect (sock, (struct sockaddr *) & remote, sizeof (remote )); If (ret <0) {printf ("connect failed ..., errno is: % d, errstring is: % s \ n ", errno, strerror (errno); return 3 ;}printf (" connect success... \ n "); char buf [1024]; while (1) {// read data from standard input into buf, and then write it into memset (buf, '\ 0 ', sizeof (buf); printf ("client: #"); fflush (stdout); ssize_t _ s = read (0, buf, sizeof (buf)-1 ); fflush (stdin); if (_ s <0) {perror ("read \ n"); break;} buf [_ s] = '\ 0'; write (sock, buf, strlen (buf); if (Strcmp (buf, "quit") = 0) {printf ("quit! \ N "); break;} _ s = read (sock, buf, sizeof (buf); if (_ s> 0) {buf [_ s] = '\ 0'; printf ("server: $ % s \ n", buf) ;}} close (sock ); printf ("sock close"); return 0 ;}
Server:
Client:
If there are any deficiencies in this article, I hope to correct them (. ⌒ ∇)
Some references:
Wu Qin http://www.cnblogs.com/skynet/
Advanced Programming in Unix environment