ssl and socket

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))  

 
 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326952403&siteId=291194637