This paper is participating in theNetwork protocols must be known and must be known”Essay campaign

Two applications if you need to communicate one of the most basic premise is to be able to show a process, only we know the IP address of the IP layer host only labels, and TCP protocol and port number identifying the host can be the only one process, so that we can use the IP address + port number + agreement only a process identified in the network. Once processes in the network can be uniquely identified, they can communicate using sockets, which are described in this article.

What is a socket

What is a socket? Socket is an abstract layer between the application layer and the transmission layer. It abstracts the complex operation of TCP/IP layer into several simple interfaces for the application layer to call to realize the process communication in the network.

The purpose of learning network programming is to develop software based on Internet communication, whether it is BS architecture or CS architecture. We develop Internet communication software is in the TCP/IP five-layer protocol application layer, when it comes to data transmission through the Internet, it needs to use the Socket abstraction layer. The socket abstraction layer does not belong to the TCP/IP layer 5, but is an abstraction to help us encapsulate the other layers including the transport layer. It hides the complex TCP/IP protocol family behind the Socket interface. For the user, a simple set of interfaces is all, allowing the Socket to organize data to conform to the specified protocol. In the development, only need to follow the provisions of the socket code, written procedures naturally follow the TCP/UDP protocol.

History and classification of sockets

The socket originated from the 1970s version of Unix at the University of California, Berkeley, known as BSD Unix. For this reason, sockets are sometimes also referred to as “Berkeley sockets” or “BSD sockets”. Initially, sockets were designed to communicate between multiple applications on the same host. This is also called interprocess communication, or IPC. There are two types (or races) of sockets, file-based and network-based.

Socket family based on file type

AF_UNIX is the name of the socket family. In Unix, everything is a file, and the file-based socket calls the underlying file system to fetch data. Two socket processes running on the same machine can communicate indirectly by accessing the same file system.

A family of sockets based on network types

Socket family names: AF_INET and AF_INET6 are used for ipv6, and there are other address families that are either platform specific, deprecated, rarely used, or not implemented at all. AF_INET is the most widely used of all address families. Python supports many address families, but since we’re only concerned with network programming, we’ll use AF_INET most of the time.

The socket module

Socket can realize two processes in the network to communicate, we can compare the working process of socket from the daily life example, for example, you want to call a friend, dial first, the friend hears the phone ring, then you and your friend set up a connection, you can talk. When the conversation is over, hang up the phone and end the conversation. Scenarios in life explain how sockets work.

Server:

1 Initialize the socket object

2 Bind the SOCKET Object to the IP address and port of the server (BIND)

3 Listening to ports

Client:

1 Initialize the socket object

2 Connecting to the Server

3 If the connection is successful, the connection between the client and server is established

After successful connection:

1 The client sends a data request

2 The server receives and processes the request

3 Reply data is sent to the client, and the client reads the data

4 Close the connection. An interaction ends

The Socket module in Python encapsulates the Socket abstraction layer and provides some simple interfaces to help us do this. The following code describes the basic use of the socket module:

Add: How to view source code in Python: Hold Down CTRL, hover the mouse over the object to view source code, and click to see the source code.

Socket_family can be AF_UNIX or AF_INET. Socket_type can be SOCK_STREAM or SOCK_DGRAM. The default value of socket_family is -1, indicating AF_INET, and the default value of socket_type is -1, indicating SOCK_STREAM. Can be viewed through the source code
socket.socket(socket_family, socket_type, protocal=0)

Get a TCP/IP socket
tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_sock = socket.socket()  The default value is -1

Get udp/ IP socket
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Copy the code

The socket module provides different methods for the client program and the server program to send and receive data, and also provides some common methods. Knowing the usage of socket module, we can develop client and server small programs based on TCP/UDP protocol according to socket module. Let’s take it one by one:

First, the socket module provides methods for the server:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  Get the socket object
s.bind()   Bind host, port number to socket
s.listen()  Start TCP listening
s.accept()  Passively accept TCP client connections and wait for them to arrive
Copy the code

The socket module provides the following methods for the client:

import socket

s = socket.socket()  # socket object
s.connect()  Initializes the connection to the TCP server
s.connect_ex()  An extended version of the # connect() function that returns an error code instead of throwing an exception
Copy the code

Finally, there are some common methods that sockets provide for both clients and servers:

s.recv()   Receive TCP data
s.send()   If the amount of data to be sent exceeds the remaining space of the cache, the data will be lost and the data will not be sent completely.
s.sendall()   If the amount of data to be sent is greater than the remaining space of the cache, the data will not be lost, and sendall will be called repeatedly until the whole data is sent.
s.recvfrom()   Receive UDP data
s.sendto()   Send UDP data
s.getpeername()   The remote address to connect to the current socket
s.getsockname()   Address of the current socket
s.getsockopt()   # return parameters for the specified socket
s.setsockopt()   Set the parameters of the specified socket
s.close()   Close the socket connection
Copy the code

Tcp-based sockets

TCP is based on bidirectional connections, so the server must be started first and then the client must be started to connect to the server.

Start with the server side: the server side initializes the Socket, then binds the port, listens to the port, calls accept to block, and waits for the client to connect.

At this time, if a client initializes a Socket and then connects to the server (CONNECT), if the connection is successful, then the connection between the client and the server is established.

The client sends a data request, the server receives and processes the request, then sends the response data to the client, the client reads the data, and finally closes the connection, ending an interaction.

The above is a simple process based on TCP communication. According to the comparison of the principle of calling, the specific code is as follows:

Simple VERSION TCP communication

Server file:

# sever. Py Server file
# Take making and receiving calls on your cell phone
import socket

# 1 Buy mobile phone: Obtain the object of data sent and received by the server
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 2 Mobile phone card -- bind IP+ port to server
phone.bind(('127.0.0.1'.6666))

# 3 Startup - The server is listening
phone.listen(3)  A half-connection pool can hold only three pending connection requests

# 4 Waiting for incoming calls: The server establishes a connection with the client. After establishing a connection, the server can obtain the TCP connection channel information, IP address and interface of the client
conn,client_addr = phone.accept()
print(conn)
print('Client IP and Interface',client_addr)

# 5 The phone is connected, and the two chat happily -- sending and receiving data
data = conn.recv(1024)  The maximum number of bytes to be received is 1024
print('Client message', data.decode('utf-8'))
conn.send(data.upper())

# 6 Hang up -- Disconnect from the client
conn.close()

# 7 Phone Shutdown - The server is shut down
phone.close()
Copy the code

Client file

# client.py
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect(('127.0.0.1'.6666))

phone.send('show information'.encode('utf-8'))

data = phone.recv(1024)

print('Server',data.decode('utf-8'))

phone.close()
Copy the code

TCP loop communication

The above simple version of the communication code, there is a problem, that is, it took a lot of effort to connect successfully, the result of a message connection is disconnected, like a phone call, play once say a word, hang up the phone, if there is not finished, also need to call….

To solve the above problem, we can use loops to continuously receive or send data-loop communication

Server file:

# sever.py
import socket

# 1 Buy a phone - get the server
phone = socket.socket()

# 2 Mobile phone card -- Determine the IP and port of the server
phone.bind(('127.0.0.1'.7890))

# 3 Mobile phone startup -- The server enters the listening state
phone.listen(3)

# 4 Wait to answer the call. Obtain the IP address and port of the TCP channel and client

conn,client_addr = phone.accept()

# 5 Call -- Send and receive data
while True:
    # Exception handling: server crashes when preventing client from suddenly shutting down
    try:
        data = conn.recv(1024)
        if not data:break
        print('Client',data.decode('utf-8'))

        conn.send(data.upper())
    except Exception:
        break

# 6 Hang up - The server is disconnected from the client channel
conn.close()

# 7 Shutdown - The server is shut down
phone.close()
Copy the code

Client file:

# client.py
import socket

phone = socket.socket()
phone.connect(('127.0.0.1'.7890))
while True:
    info = input('> >').strip()
    # When the length of the data sent is 0, both the server and client will enter the blocking stage waiting for the data to be received. Therefore, judge whether the length of the information input by the user is 0 after being processed by the strip
    if len(info) == 0:continue
    if info == 'q': break
    phone.send(info.encode('utf-8'))
    data = phone.recv(1024)
    print('Server', data.decode('utf-8'))

phone.close()
Copy the code

TCP communication that the server does not close

The above code scheme also has problems, as a server should meet two conditions, one is to provide services for the client all the time, the other is to provide services concurrently, the above circular communication when the client is disconnected, the server will also stop running, it can no longer provide services to other clients. So we can keep the server on. The following is the optimized server code:

# sever.py
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.bind(('127.0.0.1'.65447))

phone.listen(5)
while True:
    conn, client_addr = phone.accept()
    while True:
        try:
            data = conn.recv(1024)
            if not data:break
            print('Client',data.decode('utf-8'))
            conn.send(data.upper())
        except Exception:
            break

    conn.close()  # disconnect the client from the server
Copy the code

Of course, the above code also needs to be optimized, that is, the server can only serve a client at the same time, if you want to allow the server to serve multiple servers, we will learn later knowledge – concurrent programming. I won’t go into too much detail here.

When TCP communication is used, problems may occur if the client sends empty messages. If the client sends an empty message, the message is not actually sent, but the application sends the message to the operating system. The operating system receives the message and finds that it is empty and does not send it out. Therefore, the client enters the recV and other state after sending empty, and at this time, the server does not receive any message and will not reply, so it falls into the awkward two-side waiting state. The solution is to determine whether the message sent by the client is empty and skip to the next loop without allowing the empty message to be sent to the operating system.

Udp-based sockets

Udp is linkless, so no error will be reported if either end is started first. Therefore, it is much simpler to use than tcp-based sockets.

UDP is a datagram protocol, and sends a null header. Therefore, when the client enters a null header, the server can also receive it.

Udp-based sockets are not connected. Both the client and server can start socket communication first or end the communication in advance. That is, the client’s departure does not affect the normal operation of the server.

Server code

# server.py
import socket

ip_port = ('127.0.0.1'.8080)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(ip_port)
print('The server starts listening...... ')

while True:
    get_msg, client_addr = server.recvfrom(1024)
    print('from client:', get_msg)
    server.sendto(get_msg.upper(), client_addr)

# server.close()
Copy the code

Client code

import socket

ip_port = ('127.0.0.1'.8080)
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

while 1:
    msg = input('> > > :').strip()
    if msg == 'q':
        break
    if not msg:		Udp socket supports null packets sent by clients, and server can also receive null packets
        continue
    client.sendto(msg.encode('utf-8'), ip_port)
    get_msg, server_addr = client.recvfrom(1024)
    print(get_msg.decode('utf-8'))                     Close the client socket
Copy the code

conclusion

The article was first published on the wechat public account Program Yuan Xiaozhuang, and synchronized with nuggets and Zhihu.

The code word is not easy, reprint please explain the source, pass by the little friends of the lovely little finger point like and then go (╹▽╹)