Socket programming practices in Linux (2) introduction to basic socket programming APIs
What is Socket?
Socket originated from Unix, and one of the basic philosophies of Unix/Linux is "Everything is a file". You can use the "open-> read/write-> close" mode to operate. Socket is an implementation of this mode. socket is a special file, and some socket functions are operations (read/write IO, open, and close) on it ). To put it bluntly, Socket is an intermediate software abstraction layer for communications between the application layer and the TCP/IP protocol family. It is a set of interfaces. In the design mode, Socket is actually a facade mode, which hides the complex TCP/IP protocol family behind the Socket interface. for users, a set of simple interfaces are all, let the Socket organize the data to conform to the specified protocol.
A Socket can be considered as an interface between a user process and the kernel network protocol stack (as shown in Figure). It can be used not only for inter-local process communication, it can be used for process communication between different hosts on the network, or even between heterogeneous systems.
For example, the TCP/IP protocol stack is already part of the kernel and has been implemented. The Router works at the network layer (Router) and the Application needs to be implemented. A Socket can be seen as a programming interface between a user process and a kernel network protocol stack. It can be seen as a way of inter-process communication. Unlike a pipe, it is full-duplex, it can be used for process communication between the local machine and different hosts, and also for heterogeneous communication. (For example, mobile phones and computers are in the ARM and X86 architectures respectively)
Pv4 interface address Structure
The IPv4 address structure is also known as the "Internet socket address structure". It is named after "sockaddr_in" and is defined in the header file. Medium
Struct sockaddr_in {uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; // 2 bytes struct in_addr sin_addr; // 4 bytes char sin_zero [8]; // 8 bytes };
Member description:
Sin_len: the length of the entire sockaddr_in struct. The first member before 4.3BSD-Reno is sin_family.
Sin_family: Specifies the address family. For IPv4, it must be set to AF_INET (Socket can be used not only for TCP/IP but also for UNIX domain protocols)
Sin_port: Port
Sin_addr: IPv4 address;
Sin_zero: not used for the moment. It is usually set to 0.
General address Structure
Used to specify the address associated with the socket (other protocols are supported ).
Struct sockaddr {uint8_t sin_len; sa_family_t sin_family; char sa_data [14]; // 14 bytes };
Note:
Sin_len: length of the entire sockaddr struct
Sin_family: Specifies the address family.
Sa_data: It is determined by sin_family.
Note: when used, the IPv4 address structure is forcibly converted to a general address structure, just as the above sockaddr_in is converted to sockaddr.
Network byte order
Large-end and small-end bytecode are used between heterogeneous systems.
1. Big Endian)
MSB: Most Significant Bit is stored at the Lowest memory address, and LSB: Lowest Significant Bit is stored at the highest memory address.
2. Little Endian)
MSB: Most Significant Bit is stored at the highest memory address, and LSB: Lowest Significant Bit is stored at the Lowest memory address.
3. Host byte order
Different hosts have different byte sequences. For example, x86 is a small byte sequence, Motorola 6800 is a large byte sequence, and ARM is configurable.
4. Network byte order
The Network byte sequence is defined as the large-end byte sequence.
Determine the byte order of your host?
// Test whether the current system is in the small-end mode int main () {int data = 0x12345678; // int = 4 bytes (32-bit) // every four binary digits represent one hexadecimal digit, // then the 8-bit hexadecimal digit represents 4*8 = 32-bit binary char * p = (char *) & data; printf ("% x, % x, % x, % x \ n ", p [0], p [1], p [2], p [3]); // 0x78 is low, if it is placed at p [0] (low address), it indicates that it is a small-end mode if (p [0] = 0x78) {cout <"the current system is in the small-end mode" <endl; // The x86 platform is in the small-end mode} else if (p [0] = 0x12) {cout <"the current system is in the big end mode" <endl; // IBM is in the big end mode }}
For the small-end mode, the original string output is 78 56 34 12
Byte sequence Conversion Function (usually used for port conversion)
Uint32_t htonl (uint32_t hostlong); uint16_t htons (uint16_t hostshort); uint32_t ntohl (uint32_t netlong); uint16_t ntohs (uint16_t netshort);/** Note: h stands for (local) host; n stands for network; s stands for short; l stands for long ;*/
// Test the Conversion Result: int main () {int localeData = 0x12345678; char * p = (char *) & localeData; printf ("Begin: % 0x % 0x % 0x % 0x \ n ", p [0], p [1], p [2], p [3]); // convert local bytes to network bytes int inetData = htonl (localeData); p = (char *) & inetData; printf ("After: % 0x % 0x % 0x % 0x \ n ", p [0], p [1], p [2], p [3]); if (p [0] = 0x12) cout <"the network system is in big-end mode" <endl; else cout <"the network system is in small-end mode" <endl; printf ("host: % x, inet: % x \ n", localeData, inetData );}
Address translation function (for IP address translation)
# Include
# Include int inet_aton (const char * cp, struct in_addr * indium); in_addr_t inet_addr (const char * cp); char * inet_ntoa (struct in_addr in); // The definition of in_addr is as follows: typedef uint32_t in_addr_t; struct in_addr {in_addr_t s_addr ;};
// Practice int main () {// convert the bitwise decimal to the decimal number cout <inet_addr ("192.168.139.20.") <endl; // convert the decimal number to struct in_addr address; address. s_addr = inet_addr ("192.168.139.20."); cout <inet_ntoa (address) <endl; memset (& address, 0, sizeof (address); inet_aton (" 127.0.0.1 ", & address); cout <address. s_addr <endl; cout <inet_ntoa (address) <endl; return 0 ;}
Socket Type
1) stream socket (SOCK_STREAM)
It provides connection-oriented and reliable data transmission services, with no data errors and no repeated sending, and receives data in the sending order, corresponding to the TCP protocol.
2) datagram socket (SOCK_DGRAM)
Provides the connectionless service. No error-free guarantee is provided, data may be lost or duplicated, and the receiving order is disordered, corresponding to the UDP protocol.
3) original socket (SOCK_RAW)
This allows us to directly encapsulate the IP layer for transmission across the transport layer. (Application Layer direct and IP layer)
Socket Functions
#include
#include
int socket(int domain, int type, int protocol);
Create a socket for communication
Parameters:
Domain: Specifies the protocol family. The common value is AF_INET (IPv4)
Type: Specifies the socket type, streaming socket SOCK_STREAM, datagram socket SOCK_DGRAM, original socket SOCK_RAW
Protocol: protocol type. Common Values: 0. Use the default protocol.
Return Value:
Success: return a non-negative integer, socket;
Failed:-1 is returned.
Bind Functions
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Bind a local address to the socket
Parameters:
Sockfd: socket returned by the socket function
Addr: Address to bind
// Sockaddr_in structure, which must be forcibly converted to struct sockaddr * type 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 */};/* Internet address. */struct in_addr {uint32_t s_addr;/* address in network byte order */};
/** Example: Use INADDR_ANY to bind any address of the Local Machine **/int main () {int listenfd = socket (AF_INET, SOCK_STREAM, 0 ); if (listenfd =-1) err_exit ("socket error"); struct sockaddr_in addr; addr. sin_family = AF_INET; addr. sin_port = htons (8001); // bind any IP address of the Local Machine to use the addr statement in the following two lines. sin_addr.s_addr = htonl (INADDR_ANY); // inet_aton ("127.0.0.1", & addr. sin_addr); // addr. sin_addr.s_addr = inet_addr ("127.0.0.1"); if (bind (listenfd, (const struct sockaddr *) & addr, sizeof (addr) =-1) err_exit ("bind error"); else cout <"bind success" <endl ;}
Listen Function
int listen(int sockfd, int backlog);
The listen function should be used after the socket and bind functions are called and before the accept function is called to convert a socket from an active socket to a passive socket.
Backlog description:
For a given listening set interface, the kernel needs to maintain two queues:
1. The client has sent the handshake to the server, and the server is waiting to complete the TCP three-way handshake (SYN_RCVD status)
2. The connected Queue (ESTABLISHED status)
However, the sum of the two queue lengths cannot exceed the backlog
SOMAXCONN is recommended for backlog (this value is 128 in 3.small-44-generic), and the maximum value of the waiting queue is used;
After bind, it becomes a passive socket that accepts the connection. The active socket is used to initiate the connection, for example, connect.
Accept Function
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
Returns the first connection request on the queue of pending connections for the listening
Socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. the newly created socket is not in the listening state). If The completed connection queue is empty, blocking is performed. The original socket sockfd is unaffected by this call.
Parameters:
Sockfd: server socket
Addr: returns the socket address of the Peer. If not, it can be set to NULL.
Addrlen: return the length of the socket address of the Peer. If you don't care about it, you can set it to NULL. Otherwise, you must initialize the socket address.
Returned value: On success, these system callreturn a nonnegative integer that is a descriptor for the accepted socket. On error,-1 is returned, and errno is set appropriately.
Connect Function
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Creates a socket connected to the specified addr.
Parameters:
Sockfd: Socket not connected
Addr: the socket address to connect
Addrlen: length of the second addr Parameter
Example: echo server/client implementation
// Server code int main () {int listenfd = socket (AF_INET, SOCK_STREAM, 0); if (listenfd =-1) err_exit ("socket error "); struct sockaddr_in addr; addr. sin_family = AF_INET; addr. sin_port = htons (8001); addr. sin_addr.s_addr = htonl (INADDR_ANY); if (bind (listenfd, (const struct sockaddr *) & addr, sizeof (addr) =-1) err_exit ("bind error "); if (listen (listenfd, SOMAXCONN) =-1) err_exit ("listen error"); char buf [512]; int readBytes; struct sockaddr_in clientAddr; // remember: socklen_t addrLen = sizeof (clientAddr); while (true) {int clientfd = accept (listenfd, (struct sockaddr *) & clientAddr, & addrLen ); if (clientfd =-1) err_exit ("accept error"); // print the Client IP address and port number cout <"Client information:" <inet_ntoa (clientAddr. sin_addr) <"," <ntohs (clientAddr. sin_port) <endl; memset (buf, 0, sizeof (buf); while (readBytes = read (clientfd, buf, sizeof (buf)> 0) {cout <buf; if (write (clientfd, buf, readBytes) =-1) err_exit ("write socket error"); memset (buf, 0, sizeof (buf);} if (readBytes = 0) {cerr <"client connect closed... "<endl; close (clientfd);} else if (readBytes =-1) err_exit (" read socket error ");} close (listenfd );}
// Client code int main () {int sockfd = socket (AF_INET, SOCK_STREAM, 0); if (sockfd =-1) err_exit ("socket error "); // enter the server port number and IP address struct sockaddr_in serverAddr; serverAddr. sin_family = AF_INET; serverAddr. sin_port = htons (8001); serverAddr. sin_addr.s_addr = inet_addr ("127.0.0.1"); if (connect (sockfd, (const struct sockaddr *) & serverAddr, sizeof (serverAddr) =-1) err_exit ("connect error"); char B Uf [512]; while (fgets (buf, sizeof (buf), stdin )! = NULL) {if (write (sockfd, buf, strlen (buf) =-1) err_exit ("write socket error"); memset (buf, 0, sizeof (buf); int readBytes = read (sockfd, buf, sizeof (buf); if (readBytes = 0) {cerr <"server connect closed... \ nexiting... "<endl; break;} else if (readBytes =-1) err_exit (" read socket error "); cout <buf; memset (buf, 0, sizeof (buf);} close (sockfd );}