First exposure to this problem was due to tornado. In tornado.netutil, I found the function of wrapping socket into ssl socket. It can be written like this. Well, my knowledge is shallow.
Socket
The encapsulation and application of the TCP/IP protocol by socket is at the programmer level. Regarding the protocol, I recommend the "Protocol Forest" on Douban, which is very good and suitable for understanding.
python socket:
__init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
family is the protocol domain, also known as the protocol family. The protocol family determines the address type of the socket, and the corresponding address must be used in communication
Commonly used are:
AF_INET decides to use ipv4 address 32 bits, and port number (combination of 16 bits)
AF_INET6
AF_LOCAL or AF_UNIX UNIX domain socket AF_UNIX decides to use an absolute path as the address
AF_ROUTE
type is the type of socket
Commonly used are:
SOCK_STREAM
SOCK_DGRAM
SOCK_RAW
SOCK_PACKET
SOCK_SEQPACKET
proto specifies the protocol
IPPROTO_TCP tcp protocol
IPPROTO_UDP udp protocol
IPPROTO_SCTP sctp protocol
IPPROTO_TIPC tipc 协议
Personally, socket is the encapsulation of the transmission protocol. Using socket can not pay attention to the processing of protocol data such as tcp, which is closer to the programmer.
Socket can also be used in combination with epoll, refer to http://blog.csdn.net/songfreeman/article/details/51179213:
#!/usr/bin/env python import select import socket response = b'' serversocket = socket.socket (socket.AF_INET, socket.SOCK_STREAM) serversocket.bind(('0.0.0.0', 8080)) serversocket.listen(1) # Because sockets are blocking by default, you need to use non-blocking (asynchronous) mode. serversocket.setblocking(0) # Create an epoll object epoll = select.epoll() # Register attention to read events on the server socket. A read event will trigger the server socket at any time to receive a socket connection epoll.register(serversocket.fileno(), select.EPOLLIN) try: # Dictionary connections maps file descriptors (integers) to their corresponding network connection objects connections = {} requests = {} responses = {} while True: # Query the epoll object to see if any events of interest are fired. The parameter "1" means that we will wait 1 second to see if an event occurs. # If any events of interest occurred before this query, this query will return immediately with a list of those events events = epoll.poll(1) # event is returned as a tuple of sequences (fileno, event code). fileno is synonymous with file descriptor and is always an integer. for fileno, event in events: # If the server generates an event, it means that there is a new connection coming in if fileno == serversocket.fileno(): connection, address = serversocket.accept() print('client connected:', address) # Set the new socket to non-blocking mode connection.setblocking(0) # Register attention to read (EPOLLIN) events for new sockets epoll.register(connection.fileno(), select.EPOLLIN) connections[connection.fileno()] = connection # Initialize the received data requests[connection.fileno()] = b'' # If a read event occurs, read the new data sent from the client elif event & select.EPOLLIN: print("------recvdata---------") # Receive the data sent by the client requests[fileno] += connections[fileno].recv(1024) # If the client exits, close the client connection and cancel all read and write listeners if not requests[fileno]: connections[fileno].close() # Delete the listener object in the connections dictionary del connections[fileno] # Delete the handle object corresponding to the received data dictionary del requests[connections[fileno]] print(connections, requests) epoll.modify(fileno, 0) else: # Once the completion request has been received, unregister attention on the read event and register attention on the write (EPOLLOUT) event. When the write event occurs, it will reply data to the client epoll.modify(fileno, select.EPOLLOUT) # Print the complete request, proving that although the communication with the client is interleaved, the data can be assembled and processed as a whole print('-' * 40 + '\n' + requests[fileno].decode()) # If a write event occurs on a client socket, it will accept new data to send to the client elif event & select.EPOLLOUT: print("-------send data---------") # Send a portion of the response data each time until the complete response data has been sent to the operating system for transmission to the client byteswritten = connections[fileno].send(requests[fileno]) requests[fileno] = requests[fileno][byteswritten:] if len(requests[fileno]) == 0: # Once the complete response data is sent, no longer focus on writing events epoll.modify(fileno, select.EPOLLIN) # HUP (hanging) event indicates that the client socket has been disconnected (ie closed), so the server also needs to be closed. # There is no need to register attention to HUP events. On sockets, they are always registered with the epoll object elif event & select.EPOLLHUP: print("end hup------") # Unregister concerns about this socket connection epoll.unregister(fileno) # close the socket connection connections[fileno].close() del connections[fileno] finally: # Open socket connections do not need to be closed, because Python will close them when the program ends. Explicit closing here is a good code practice epoll.unregister(serversocket.fileno()) epoll.close() serversocket.close()
SSL
ssl personally like to understand it as an encryption protocol. Encrypted using a symmetric algorithm. Versions 1 and 2 of the SSL protocol only provide server authentication, and version 3 adds client authentication, which requires both client and server digital certificates.
Pirated image: refer to https://www.wosign.com/Basic/howsslwork.htm
The code that wraps socket into ssl in tornado:
#Define some parameters of SSL _SSL_CONTEXT_KEYWORDS = frozenset(['ssl_version', 'certfile', 'keyfile', 'cert_reqs', 'ca_certs', 'ciphers']) def ssl_options_to_context(ssl_options): """Attempt to convert ssl option parameter to ssl.SSLContext object""" if isinstance(ssl_options, dict): # The parameters of the ssl option must be predefined by _SSL_CONTEXT_KEYWORDS assert all(k in _SSL_CONTEXT_KEYWORDS for k in ssl_options), ssl_options if (not hasattr(ssl, 'SSLContext') or isinstance(ssl_options, ssl.SSLContext)): return ssl_options context = ssl.SSLContext( ssl_options.get('ssl_version', ssl.PROTOCOL_SSLv23)) if 'certfile' in ssl_options: context.load_cert_chain(ssl_options['certfile'], ssl_options.get('keyfile', None)) if 'cert_reqs' in ssl_options: context.verify_mode = ssl_options['cert_reqs'] if 'ca_certs' in ssl_options: context.load_verify_locations(ssl_options['ca_certs']) if 'ciphers' in ssl_options: context.set_ciphers(ssl_options['ciphers']) if hasattr(ssl, 'OP_NO_COMPRESSION'): context.options |= ssl.OP_NO_COMPRESSION return context def ssl_wrap_socket(socket, ssl_options, server_hostname=None, **kwargs): #wrapper function context = ssl_options_to_context(ssl_options) if hasattr(ssl, 'SSLContext') and isinstance(context, ssl.SSLContext): if server_hostname is not None and getattr(ssl, 'HAS_SNI'): return context.wrap_socket(socket, server_hostname=server_hostname, **kwargs) else: return context.wrap_socket(socket, **kwargs) else: return ssl.wrap_socket(socket, **dict(context, **kwargs))