We are well aware of the value of information exchange, so how do processes in the network communicate? For example, when we open the browser to browse the web every day, how does the browser process communicate with the web server? When you use QQ to chat, how does the QQ process communicate with the server or the QQ process where your friends are? Do all these rely on sockets? So what is a socket? What are the types of sockets? There are also basic functions of socket, which are what this article wants to introduce. The main contents of this article are as follows:
1. How to communicate between processes in the network?
2. What is Socket?
3. Basic operations of socket
3.1, socket() function
3.2, bind() function
3.3, listen(), connect() function
3.4, accept() function
3.5, read( ), write() function, etc.
3.6. close() function
4. Detailed explanation of TCP’s three-way handshake to establish a connection in socket
5. Detailed explanation of TCP’s four-way handshake to release connection in socket
6. An example (Practice (For a moment)
7. Leave a question, everyone is welcome to reply! ! !
1. How to communicate between processes in the network?
There are many ways of local inter-process communication (IPC), but they can be summarized into the following 4 categories:
Message passing (pipeline, FIFO, message queue)
Synchronization (mutex, condition variable, read-write lock, File and write record locks, semaphores)
Shared memory (anonymous and named)
Remote procedure calls (Solaris gates and Sun RPC)
But these are not the topic of this article! What we are going to discuss is how to communicate between processes in the network? The first problem to solve is how to uniquely identify a process, otherwise communication will be impossible! A process can be uniquely identified locally by the process PID, but this does not work on the network. In fact, the TCP/IP protocol suite has helped us solve this problem. The "ip address" of the network layer can uniquely identify the host in the network, while the "protocol + port" of the transport layer can uniquely identify the application (process) in the host. In this way, the triplet (ip address, protocol, port) can be used to identify the network process, and process communication in the network can use this mark to interact with other processes.
Applications that use the TCP/IP protocol usually use application programming interfaces: sockets of UNIX BSD and TLI of UNIX System V (already obsolete) to achieve communication between network processes. For now, almost all applications use sockets, and now is the Internet era. Process communication on the network is ubiquitous. This is why I say "everything is socket".
2. What is Socket?
We already know above that processes in the network communicate through sockets, so what is a socket? Socket originated from Unix, and one of the basic philosophies of Unix/Linux is that "everything is a file" and can be operated in the "open -> read and write write/read -> close" mode. My understanding is that Socket is an implementation of this mode. Socket is a special file, and some socket functions are operations on it (read/write IO, open, close). We will introduce these functions later.
The origin of the word socket
The first use in the field of networking was found in the document IETF RFC33 released on February 12, 1970, written by Stephen Carr, Steve Crocker and Vint Cerf. According to the Computer History Museum, Croker wrote: "Elements of a namespace may be called socket interfaces. A socket interface forms one end of a connection, and a connection may be fully specified by a pair of socket interfaces. "The Computer History Museum added: "This is about 12 years earlier than BSD's socket interface definition."
3. Basic operation of socket
Since socket is part of the "open-write/read-close" mode. implementation, then the socket provides functional interfaces corresponding to these operations. The following uses TCP as an example to introduce several basic socket interface functions.
3.1, socket() function
int socket(int domain, int type, int protocol);
socket function corresponds to the opening operation of ordinary files. The ordinary file open operation returns a file descriptor, and socket() is used to create a socket descriptor (socket descriptor), which uniquely identifies a socket. This socket descriptor is the same as the file descriptor. It is used in subsequent operations. It is used as a parameter to perform some read and write operations.
Just like you can pass different parameter values ??to fopen to open different files. When creating a socket, you can also specify different parameters to create different socket descriptors. The three parameters of the socket function are:
domain: the protocol domain, also known as the protocol family. Commonly used protocol families include AF_INET, AF_INET6, AF_LOCAL (or AF_UNIX, Unix domain socket), AF_ROUTE, etc. The protocol family determines the address type of the socket, and the corresponding address must be used in communication. For example, AF_INET determines to use a combination of ipv4 address (32-bit) and port number (16-bit), and AF_UNIX determines to use an absolute path. Name as address.
type: Specify the socket type. Commonly used socket types include SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_PACKET, SOCK_SEQPACKET, etc. (What are the types of sockets?).
protocol: Hence the name, it means a designated protocol. Commonly used protocols include IPPROTO_TCP, IPPTOTO_UDP, IPPROTO_SCTP, IPPROTO_TIPC, etc., which respectively correspond to the TCP transmission protocol, UDP transmission protocol, STCP transmission protocol, and TIPC transmission protocol (I will discuss this protocol separately!).
Note: The above type and protocol cannot be combined at will. For example, SOCK_STREAM cannot be combined with IPPROTO_UDP. When protocol is 0, the default protocol corresponding to the type type is automatically selected.
When we call socket to create a socket, the returned socket descriptor exists in the protocol family (address family, AF_XXX) space, but does not have a specific address. If you want to assign an address to it, you must call the bind() function, otherwise the system will automatically assign a port randomly when calling connect() or listen().
3.2. bind() function
As mentioned above, the bind() function assigns a specific address in an address family to the socket. For example, corresponding to AF_INET and AF_INET6, an ipv4 or ipv6 address and port number combination is assigned to the socket.
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); The three parameters of the
function are:
sockfd: the socket descriptor, which is created through the socket() function and uniquely identifies it. a socket. The bind() function will bind a name to this descriptor.
addr: a const struct sockaddr * pointer pointing to the protocol address to be bound to sockfd. This address structure varies according to the address protocol family when the address creates the socket. For example, ipv4 corresponds to:
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 */}; ipv6對應(yīng)的是: 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) */ };struct in6_addr { unsigned char s6_addr[16]; /* IPv6 address */ }; Unix域?qū)?yīng)的是: #define UNIX_PATH_MAX 108struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[UNIX_PATH_MAX]; /* pathname */ }; addrlen:對應(yīng)的是地址的長度。
Usually the server will bind a well-known address (such as ip address + port number) when it is started, for To provide services, customers can connect to the server through it; the client does not need to specify, the system automatically assigns a port number and its own IP address combination. This is why the server usually calls bind() before listening, but the client does not call it. Instead, the system randomly generates one during connect().
Network byte order and host byte order
Host byte order is what we usually call big endian and little endian modes: different CPUs have different byte order types, these byte order refers to the integers in memory The order in which they are saved is called host order. The standard definitions of Big-Endian and Little-Endian are quoted as follows:
a) Little-Endian means that the low-order bytes are arranged at the low address end of the memory, and the high-order bytes are arranged at the high address end of the memory.
b) Big-Endian means that the high-order bytes are arranged at the low address end of the memory, and the low-order bytes are arranged at the high address end of the memory.
Network byte order: 4-byte 32-bit values ??are transmitted in the following order: first 0~7bit, then 8~15bit, then 16~23bit, and finally 24~31bit. This transfer order is called big-endian. Because all binary integers in the TCP/IP header are required to be in this order when transmitted over the network, it is also called network byte order. Byte order, as the name suggests, is the order in which data larger than one byte is stored in memory. There is no order issue with data of one byte.
So: When binding an address to a socket, please first convert the host byte order to network byte order, and do not assume that the host byte order uses Big-Endian like the network byte order. There have been murders caused by this problem! This problem has caused many inexplicable problems in the company's project code, so please remember not to make any assumptions about the host byte order, and be sure to convert it into network byte order before assigning it to the socket.
3.3, listen(), connect() function
If you are a server, after calling socket() and bind(), you will call listen() to listen to the socket. If the client calls connect() to issue a connection request, the server will receive the request.
int listen(int sockfd, int backlog);int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
The first parameter of the listen function is the socket descriptor to be listened to, and the second The parameter is the maximum number of connections that can be queued by the corresponding socket. The socket created by the socket() function is an active type by default, and the listen function changes the socket to a passive type, waiting for the client's connection request.
The first parameter of the connect function is the client's socket descriptor, the second parameter is the server's socket address, and the third parameter is the length of the socket address. The client establishes a connection with the TCP server by calling the connect function.
3.4, accept() function
After the TCP server calls socket(), bind(), and listen() in sequence, it will listen to the specified socket address. After calling socket() and connect() in sequence, the TCP client sends a connection request to the TCP server. After the TCP server listens to this request, it will call the accept() function to receive the request, so that the connection is established. Then you can start network I/O operations, which are similar to ordinary file read and write I/O operations.
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); The first parameter of the
accept function is the socket descriptor of the server, and the second parameter is a pointer to struct sockaddr * for return The protocol address of the client. The third parameter is the length of the protocol address. If accpet succeeds, the return value is a new descriptor automatically generated by the kernel, representing the TCP connection to the returning client.
Note: The first parameter of accept is the server's socket descriptor, which is generated when the server starts calling the socket() function, which is called the listening socket descriptor; and the accept function returns the connected socket descriptor. A server usually only creates a listening socket descriptor, which exists throughout the life cycle of the server. The kernel creates a connected socket descriptor for each client connection accepted by the server process. When the server completes serving a client, the corresponding connected socket descriptor is closed.
3.5, read(), write() and other functions
Everything is needed but the east wind is needed. At this point, the connection between the server and the client has been established. Network I/O can be called for read and write operations, which means communication between different processes in the network is realized! Network I/O operations have the following groups:
read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom( )/sendto()
I recommend using the recvmsg()/sendmsg() function. These two functions are the most common I/O functions. In fact, you can replace all the other functions above with these two functions. Their declarations are as follows:
? ? #include
? ssize_t read(int fd, void *buf, size_t count);
? ssize_t write(int fd, const void *buf, size_t count);
? # include
? ? ? ? #include
? ? ssize_t send(int sockfd, const void *buf, size_t len, int flags);
? ssize_t recv(int sockfd, 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 rec vfrom(int sockfd, void *buf, size_t len , int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int so ckfd, struct msghdr *msg, int flags);
The read function is responsible for reading content from fd. When the read is successful, read returns the actual number of bytes read. If the returned value is 0, it means that the end of the file has been read. If it is less than 0, it means that an error has occurred. If the error is EINTR, it means that the read was caused by an interrupt. If it is ECONNREST, it means there is a problem with the network connection.
The write function writes the nbytes bytes content in buf to the file descriptor fd. When successful, it returns the number of bytes written. On failure, -1 is returned and the errno variable is set. In network programs, there are two possibilities when we write to the socket file descriptor. 1) The return value of write is greater than 0, indicating that part or all of the data has been written. 2) The returned value is less than 0, and an error occurred. We have to deal with it according to the error type. If the error is EINTR, it means that an interrupt error occurred during writing. If it is EPIPE, it means there is a problem with the network connection (the other party has closed the connection).
I will not introduce these pairs of I/O functions one by one. For details, please refer to the man document or Baidu or Google. Send/recv will be used in the following example.
3.6, close() function
在服務(wù)器與客戶端建立連接之后,會進(jìn)行一些讀寫操作,完成了讀寫操作就要關(guān)閉相應(yīng)的socket描述字,好比操作完打開的文件要調(diào)用fclose關(guān)閉打開的文件。
#include
close一個TCP socket的缺省行為時把該socket標(biāo)記為以關(guān)閉,然后立即返回到調(diào)用進(jìn)程。該描述字不能再由調(diào)用進(jìn)程使用,也就是說不能再作為read或write的第一個參數(shù)。
注意:close操作只是使相應(yīng)socket描述字的引用計(jì)數(shù)-1,只有當(dāng)引用計(jì)數(shù)為0的時候,才會觸發(fā)TCP客戶端向服務(wù)器發(fā)送終止連接請求。
4、socket中TCP的三次握手建立連接詳解
我們知道tcp建立連接要進(jìn)行“三次握手”,即交換三個分組。大致流程如下:
客戶端向服務(wù)器發(fā)送一個SYN J
服務(wù)器向客戶端響應(yīng)一個SYN K,并對SYN J進(jìn)行確認(rèn)ACK J+1
客戶端再想服務(wù)器發(fā)一個確認(rèn)ACK K+1
只有就完了三次握手,但是這個三次握手發(fā)生在socket的那幾個函數(shù)中呢?請看下圖:
圖1、Detailed explanation of Socket
從圖中可以看出,當(dāng)客戶端調(diào)用connect時,觸發(fā)了連接請求,向服務(wù)器發(fā)送了SYN J包,這時connect進(jìn)入阻塞狀態(tài);服務(wù)器監(jiān)聽到連接請求,即收到SYN J包,調(diào)用accept函數(shù)接收請求向客戶端發(fā)送SYN K ,ACK J+1,這時accept進(jìn)入阻塞狀態(tài);客戶端收到服務(wù)器的SYN K ,ACK J+1之后,這時connect返回,并對SYN K進(jìn)行確認(rèn);服務(wù)器收到ACK K+1時,accept返回,至此三次握手完畢,連接建立。
總結(jié):客戶端的connect在三次握手的第二個次返回,而服務(wù)器端的accept在三次握手的第三次返回。
5、socket中TCP的四次握手釋放連接詳解
上面介紹了socket中TCP的三次握手建立過程,及其涉及的socket函數(shù)。現(xiàn)在我們介紹socket中的四次握手釋放連接的過程,請看下圖:
圖2、Detailed explanation of Socket
圖示過程如下:
某個應(yīng)用進(jìn)程首先調(diào)用close主動關(guān)閉連接,這時TCP發(fā)送一個FIN M;
另一端接收到FIN M之后,執(zhí)行被動關(guān)閉,對這個FIN進(jìn)行確認(rèn)。它的接收也作為文件結(jié)束符傳遞給應(yīng)用進(jìn)程,因?yàn)镕IN的接收意味著應(yīng)用進(jìn)程在相應(yīng)的連接上再也接收不到額外數(shù)據(jù);
一段時間之后,接收到文件結(jié)束符的應(yīng)用進(jìn)程調(diào)用close關(guān)閉它的socket。這導(dǎo)致它的TCP也發(fā)送一個FIN N;
接收到這個FIN的源發(fā)送端TCP對它進(jìn)行確認(rèn)。
這樣每個方向上都有一個FIN和ACK。
6、一個例子(實(shí)踐一下)
說了這么多了,動手實(shí)踐一下。下面編寫一個簡單的服務(wù)器、客戶端(使用TCP)——服務(wù)器端一直監(jiān)聽本機(jī)的6666號端口,如果收到連接請求,將接收請求并接收客戶端發(fā)來的消息;客戶端與服務(wù)器端建立連接并發(fā)送一條消息。
服務(wù)器端代碼:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define MAXLINE 4096 int main(int argc, char** argv) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[4096]; int n; if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ printf("create socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(6666); if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } if( listen(listenfd, 10) == -1){ printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("======waiting for client's request======\n"); while(1){ if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){ printf("accept socket error: %s(errno: %d)",strerror(errno),errno); continue; } n = recv(connfd, buff, MAXLINE, 0); buff[n] = '\0'; printf("recv msg from client: %s\n", buff); close(connfd); } close(listenfd); }
當(dāng)然上面的代碼很簡單,也有很多缺點(diǎn),這就只是簡單的演示socket的基本函數(shù)使用。其實(shí)不管有多復(fù)雜的網(wǎng)絡(luò)程序,都使用的這些基本函數(shù)。上面的服務(wù)器使用的是迭代模式的,即只有處理完一個客戶端請求才會去處理下一個客戶端的請求,這樣的服務(wù)器處理能力是很弱的,現(xiàn)實(shí)中的服務(wù)器都需要有并發(fā)處理能力!為了需要并發(fā)處理,服務(wù)器需要fork()一個新的進(jìn)程或者線程去處理請求等。
7、動動手
留下一個問題,歡迎大家回帖回答?。?!是否熟悉Linux下網(wǎng)絡(luò)編程?如熟悉,編寫如下程序完成如下功能:
服務(wù)器端:
接收地址192.168.100.2的客戶端信息,如信息為“Client Query”,則打印“Receive Query”
客戶端:
Send the information "Client Query test", "Cleint Query", "Client Query Quit" sequentially to the server at address 192.168.100.168, and then exit.
The IP address appearing in the question can be determined according to the actual situation.

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)