Socket编程示例01--Socket Server

# -*- coding:utf-8 -*-

"""
@Author:        clhon@qq.com
@Create Date:   2018-06-13
@Update Date:   2018-06-14
@Version:       V0.9.20180614
"""

import re
import time
import socket
import logging
import datetime
import threading
import socketserver

MAX_THREAD_NUM = 20

MIN_SLEEP_INTERVAL = 5
MAX_SLEEP_INTERVAL = 10

MIN_SEND_TIME = 5
MAX_SEND_TIME = 10

SOCKET_SERVER_ADDRESS = ('127.0.0.1', 5000)

THREAD_LOCK_FLAG = True
G_THREAD_LOCK = threading.Lock()

# log level
LOGGING_LEVEL = logging.INFO


def logging_config(logging_level):
    # log_format = "%(asctime)s - %(levelname)s - %(message)s"
    # log_format = "%(asctime)s [line: %(lineno)d] - %(levelname)s - %(message)s"
    log_format = "[%(asctime)s - [File: %(filename)s line: %(lineno)d] - %(levelname)s]: %(message)s"
    logging.basicConfig(level=logging_level, format=log_format)


def debug_print(msg):
    # global G_THREAD_LOCK

    if THREAD_LOCK_FLAG:
        G_THREAD_LOCK.acquire()
        print(msg)
        G_THREAD_LOCK.release()
    else:
        print(msg)


class ResponseRequestHandler(threading.Thread):
    def __init__(self, request, client_address):
        super().__init__()
        self.request = request
        self.client_address = client_address

    def response_request(self):
        while True:
            recv_data = self.request.recv(1024)
            if recv_data:
                debug_print('Received Msg: {}'.format(recv_data.decode()))
            send_data = 'Response from socket server with threading {}...'.format(self.name)

            # https://stackoverflow.com/questions/180095/how-to-handle-a-broken-pipe-sigpipe-in-python/180922#180922
            # https://stackoverflow.com/questions/11866792/how-to-prevent-errno-32-broken-pipe
            # https://stackoverflow.com/questions/41014252/socket-error-errno-32-broken-pipe/41015359
            # https://stackoverflow.com/questions/409783/socket-shutdown-vs-socket-close
            # https://www.programcreek.com/python/example/3678/socket.SHUT_RDWR
            # https://pymotw.com/2/socket/tcp.html
            # https://pythontips.com/2013/08/06/python-socket-network-programming/
            # https://docs.python.org/3/howto/sockets.html
            # https://docs.python.org/3/library/socket.html
            try:
                self.request.sendall(send_data.encode())
            except ConnectionError as err:
                # self.socket.shutdown(socket.SHUT_RDWR)
                # self.socket.close()
                # 客户端调用上面两条语句断开连接,这时候服务器端的发送会产生如下错误
                # Error message: [Errno 32] Broken pipe
                debug_print('Error message: {} with ip address {}'.format(err, self.client_address))

                # socket.shutdown(how)
                # Shut down one or both halves of the connection. If how is SHUT_RD, further receives are disallowed.
                # If how is SHUT_WR, further sends are disallowed. If how is SHUT_RDWR,
                # further sends and receives are disallowed.

                # 如客户端已断开连接,这时调用socket.shutdown(socket.SHUT_RDWR)会报错,
                # 但是没有试验SHUT_RD或者SHUT_WR参数有没有问题,待验证
                # self.request.shutdown(socket.SHUT_RDWR)
                self.request.close()
                break

    def run(self):
        self.response_request()


def open_socket_server(server_address):
    client_thread_lists = []

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_server:
        # Running an example several times with too small delay between executions, could lead to this error:
        # OSError: [Errno 98] Address already in use
        # This is because the previous execution has left the socket in a TIME_WAIT state,
        # and can’t be immediately reused.
        # There is a socket flag to set, in order to prevent this, socket.SO_REUSEADDR:
        '''
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((HOST, PORT))
        '''
        # the SO_REUSEADDR flag tells the kernel to reuse a local socket in TIME_WAIT state,
        # without waiting for its natural timeout to expire.
        socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # Bind the socket to address. The socket must not already be bound.
        socket_server.bind(server_address)

        # Enable a server to accept connections. If backlog is specified,
        # it must be at least 0 (if it is lower, it is set to 0);
        # it specifies the number of unaccepted connections that the system will allow before refusing new connections.
        # If not specified, a default reasonable value is chosen.
        socket_server.listen(20)

        while True:
            request, client_address = socket_server.accept()
            debug_print('####################Connect from {}####################'.format(client_address))
            client_thread_lists.append(ResponseRequestHandler(request, client_address))
            client_thread_lists[-1].start()
            # client_thread_lists[-1].join()


def main():
    logging_config(LOGGING_LEVEL)
    open_socket_server(SOCKET_SERVER_ADDRESS)


if __name__ == "__main__":
    print("Script start execution at {}".format(str(datetime.datetime.now())))

    time_start = time.time()
    main()

    print("\n\nTotal Elapsed Time: {} seconds".format(time.time() - time_start))
    print("\nScript end execution at {}".format(datetime.datetime.now()))

results matching ""

    No results matching ""