同步socket, 异步socket, 多线程socket, 多进程socket

开篇

socket常用,本文立足同步和异步socket,以及现有的socketserver库。

同步socket一般有利用socket库直接,就可以写出tcp或udp的套接字

socketserver提供的线程或进程方式的socket

利用python 3.5+的asyncio协议,封装一个协程的socket server ,普通的socket客户也可以连接。

Tcp 套接字

服务器端

from socket import *
phone=socket(AF_INET,SOCK_STREAM)        #套接字
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)   #解决端口占用
phone.bind(('127.0.0.1',8080))   #绑定端口和Ip到套接字
phone.listen(5)
conn,client_addr=phone.accept()   #等待tcp接受
# data1=conn.recv(10)
# print('data1: ',data1)
# data2=conn.recv(4)
# print('data2:',data2)
#接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,
#服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

客户端

from socket import *
import time
phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
# phone.send('helloworld'.encode('utf-8'))
# phone.send('egon'.encode('utf-8'))
#发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

用struct模块解决粘包问题

为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

构造报头信息

#_*_coding:utf-8_*_

import struct
import binascii
import ctypes

values1 = (1, 'abc'.encode('utf-8'), 2.7)
values2 = ('defg'.encode('utf-8'),101)
s1 = struct.Struct('I3sf')
s2 = struct.Struct('4sI')

print(s1.size,s2.size)
prebuffer=ctypes.create_string_buffer(s1.size+s2.size)
print('Before : ',binascii.hexlify(prebuffer))
# t=binascii.hexlify('asdfaf'.encode('utf-8'))
# print(t)


s1.pack_into(prebuffer,0,*values1)
s2.pack_into(prebuffer,s1.size,*values2)

print('After pack',binascii.hexlify(prebuffer))
print(s1.unpack_from(prebuffer,0))
print(s2.unpack_from(prebuffer,s1.size))

s3=struct.Struct('ii')
s3.pack_into(prebuffer,0,123,123)
print('After pack',binascii.hexlify(prebuffer))
print(s3.unpack_from(prebuffer,0))

关于struct的详细用法

服务端

import socket
import subprocess
import struct
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8088)) #绑定手机卡
phone.listen(5) #开机

print('starting...')
while True: #链接循环
    conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
    print('-------->',conn,client_addr)

    #收,发消息
    while True:#通信循环
        try:
            cmd=conn.recv(1024)
            print(cmd)
            if not cmd:break #针对linux
            #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
            #。。。。
            res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()
            print(stdout)

            #先发报头(转成固定长度的bytes类型)
            header = struct.pack('i',len(stdout)+len(stderr))
            print(header)
            conn.send(header)
            #再发送命令的结果
            conn.send(stdout)
            conn.send(stderr)
        except Exception:
            break
    conn.close() #挂电话
phone.close() #关机

客户端

import socket
import struct
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.connect(('127.0.0.1',8088)) #绑定手机卡

#发,收消息
while True:
    cmd=input('>>: ').strip()
    if not cmd:continue

    phone.send(cmd.encode('utf-8'))
    #先收报头
    header_struct=phone.recv(4)
    print(header_struct)
    unpack_res = struct.unpack('i', header_struct)
    total_size=unpack_res[0]
    print(total_size)

    #再收数据
    recv_size=0 #10241=10240+1
    total_data=b''
    while recv_size < total_size:
        recv_data=phone.recv(1024)
        print(recv_data)
        recv_size+=len(recv_data)
        print(recv_size)
        total_data+=recv_data
        print(total_data)
    # else:
    print(total_data.decode('gbk'))
phone.close()

大文件粘包问题

粘包,分包都tcp
tcp为什么会有粘包分包这些情况:
1.服务端处理不过来
2.客户端采用优化纳格尔算法,达到一定字节才发
怎么处理:
1. 客,服双方确定包头规范,根据包头的信息取包长度
2. 客户端发送带上标记位,如\n, 服务端根据标记取包

服务器端

import socket
import subprocess
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8082)) #绑定手机卡
phone.listen(5) #开机

print('starting...')
while True: #链接循环
    conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
    print('-------->',conn,client_addr)

    #收,发消息
    while True:#通信循环
        try:
            cmd=conn.recv(1024)
            if not cmd:break #针对linux
            #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
            #。。。。
            res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()
            #制作报头
            header_dic = {
                'total_size': len(stdout)+len(stderr),
                'filename': None,
                'md5': None}

            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode('utf-8')
            #发送阶段
            #先发报头长度
            conn.send(struct.pack('i',len(header_bytes)))
            #再发报头
            conn.send(header_bytes)

            #最后发送命令的结果
            conn.send(stdout)
            conn.send(stderr)
        except Exception:
            break
    conn.close() #挂电话
phone.close() #关机

客户端

import socket
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.connect(('127.0.0.1',8082)) #绑定手机卡

#发,收消息
while True:
    cmd=input('>>: ').strip()
    if not cmd:continue

    phone.send(cmd.encode('utf-8'))
    #先收报头的长度
    header_len=struct.unpack('i',phone.recv(4))[0]

    #再收报头
    header_bytes=phone.recv(header_len)
    header_json=header_bytes.decode('utf-8')
    header_dic=json.loads(header_json)
    total_size=header_dic['total_size']

    #最后收数据
    recv_size=0 #10241=10240+1
    total_data=b''
    while recv_size < total_size:
        recv_data=phone.recv(1024)
        recv_size+=len(recv_data)
        total_data+=recv_data
    print(total_data.decode('gbk'))
phone.close()

udp套接字

服务器端

from socket import *
udp_server=socket(AF_INET,SOCK_DGRAM)
udp_server.bind(('127.0.0.1',8088))

while True:
    msg,client_addr=udp_server.recvfrom(1024)
    print('has recv %s' %msg)
    udp_server.sendto(msg.upper(),client_addr)
    print('has send')
udp_server.close()

客户端

from socket import *
udp_client=socket(AF_INET,SOCK_DGRAM)

while True:
    msg=input('>>: ').strip()
    udp_client.sendto(msg.encode('utf-8'),('127.0.0.1',8088))
    print('has send')
    # res,server_addr=udp_client.recvfrom(1024)
    # print('====>',res.decode('utf-8'))

udp_client.close()

udp 套接字不会发生粘包

服务器端

from socket import *
udp_server=socket(AF_INET,SOCK_DGRAM)
udp_server.bind(('127.0.0.1',8089))

msg1,client_addr=udp_server.recvfrom(5)
print(msg1)

msg2,client_addr=udp_server.recvfrom(5)
print(msg2)

客户端

from socket import *
      udp_client=socket(AF_INET,SOCK_DGRAM)
      udp_client.sendto('hello'.encode('utf-8'),('127.0.0.1',8089))
     udp_client.sendto('world'.encode('utf-8'),('127.0.0.1',8089))

socketserver

封装了socket,而且解决了Io阻塞问题

服务端

# socketserver模块多进程,多线程
# 无论你开什么都是开线程,线程就有IO,这个模块帮你解决这个IO问题

# 基础版本,遇到问题,不能无限开线程,而且没有解决IO
# 线程开启跟进程开启一样
from socket import *
from threading import Thread
# s=socket(AF_INET,SOCK_STREAM)
# s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
# s.bind(('127.0.0.1',8090))
# s.listen(5)
# def talk(conn,addr):
#     while True: #通信循环
#         try:
#             data=conn.recv(1024)
#             if not data:break
#             conn.send(data.upper())
#         except Exception:
#             break
#     conn.close()
# if __name__ == '__main__':
#     while True:#链接循环
#         conn,addr=s.accept()
#         p = Thread(target=talk,args=(conn,addr))
#         p.start()
#     s.close()

# socketserver模块套接字,自动处理IO问题
import socketserver
class MyTCPhandler(socketserver.BaseRequestHandler):  #必须继承这个类
    def handle(self):
        # print(self.request)  打印出来的就是conn
        # print(self.client_address)   打印出来的就是addr
        while True:
            try:
                data=self.request.recv(1024)
                if not data:break
                self.request.send(data.upper())
            except Exception:
                break
        self.request.close()
if __name__ == '__main__':
    server=socketserver.ThreadingTCPServer(('127.0.0.1',8082),MyTCPhandler)  #多线程
    # 三个参数,IP,端口,类
    # server=socketserver.ForkingTCPServer(('127.0.0.1',8082),MyTCPhandler)  #多进程
    server.allow_reuse_address=True   #重用地址
    server.serve_forever()   #永远运行,就是一直开着,相当于while True

客户端

from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8082))

while True:
    msg=input('>>: ').strip()
    if not msg:continue
    client.send(msg.encode('utf-8'))
    data=client.recv(1024)
    print(data.decode("utf-8"))

高度封装,解决阻塞

# coding: utf8
__version__ = "0.4"
# get verify process

import socket
import select
import sys
import os
import errno
try:
    import threading
except ImportError:
    import dummy_threading as threading

__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
           "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
           "StreamRequestHandler","DatagramRequestHandler",
           "ThreadingMixIn", "ForkingMixIn"]
if hasattr(socket, "AF_UNIX"):
    __all__.extend(["UnixStreamServer","UnixDatagramServer",
                    "ThreadingUnixStreamServer",
                    "ThreadingUnixDatagramServer"])


def _eintr_retry(func, *args):
    """
    捕获系统中断信号并继续

    :param func:
    :param args:
    :return:
    """
    while True:
        try:
            return func(*args)
        except (OSError, select.error) as e:
            if e.args[0] != errno.EINTR:
                raise


class BaseServer:

    timeout = None

    def __init__(self, server_address, RequestHandlerClass):

        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()  # 这个事件只是再服务器停止后通知调用者
        self.__shutdown_request = False

    def server_activate(self):
        pass

    # 启动服务器的两种选择
    # 1、一次调用一次handle_request
    # 2、直接调用serve_forver

    def serve_forever(self, poll_interval=0.5):
        self.__is_shut_down.clear()  # 确保该事件初始是未被通知的
        try:
            while not self.__shutdown_request:  # 循环判断是否要关闭服务器了

                r, w, e = _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                if self in r:
                    self._handle_request_noblock()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()
    def shutdown(self):
        self.__shutdown_request = True  # 设置关闭标记
        self.__is_shut_down.wait()  # 等待关闭后回发的事件
    def handle_request(self):

        # 获取timeout
        timeout = self.socket.gettimeout()
        if timeout is None:
            timeout = self.timeout
        elif self.timeout is not None:
            timeout = min(timeout, self.timeout)

        # 只select一次
        fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
        if not fd_sets[0]:
            # 超时处理
            self.handle_timeout()
            return
        self._handle_request_noblock()
    def _handle_request_noblock(self):
        """
        这边是在select返回后调用的,所以直接获取数据是不会阻塞的

        :return:
        """

        try:
            request, client_address = self.get_request()
        except socket.error:
            return

        # 校验verify、处理process、(捕获异常并关闭)
        if self.verify_request(request, client_address):
            try:
                self.process_request(request, client_address)
            except:
                self.handle_error(request, client_address)
                self.shutdown_request(request)

    def handle_timeout(self):
        pass
    def verify_request(self, request, client_address):
        return True
    def process_request(self, request, client_address):
        """
        默认的处理流程:finish shutdown

        :param request:
        :param client_address:
        :return:
        """
        self.finish_request(request, client_address)  # 在finish中只是构造那个request handler即可
        self.shutdown_request(request)

    def server_close(self):
        pass

    def finish_request(self, request, client_address):
        self.RequestHandlerClass(request, client_address, self)
    def shutdown_request(self, request):
        self.close_request(request)
    def close_request(self, request):
        pass

    def handle_error(self, request, client_address):
        print '-'*40
        print 'Exception happened during processing of request from',
        print client_address
        import traceback
        traceback.print_exc() # XXX But this goes to stderr!
        print '-'*40


class TCPServer(BaseServer):

    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 5

    allow_reuse_address = False  # ?

    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):

        BaseServer.__init__(self, server_address, RequestHandlerClass)

        # 这边就是在baseserver中select的self中的东西
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind()  # 绑定 bind
                self.server_activate()  # 激活 listen
            except:
                self.server_close() # close
                raise

    def server_bind(self):
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()
    def server_activate(self):
        self.socket.listen(self.request_queue_size)
    def server_close(self):
        self.socket.close()
    def fileno(self):
        return self.socket.fileno()

    def get_request(self):
        """
        该函数在基类中每次select到IO后调用 所以可以无阻塞获得一个连接

        :return:
        """
        return self.socket.accept()

    def shutdown_request(self, request):
        # socket.shutdown
        try:
            request.shutdown(socket.SHUT_WR)
        except socket.error:
            pass
        self.close_request(request)
    def close_request(self, request):
        # socket.close
        request.close()


class UDPServer(TCPServer):

    allow_reuse_address = False  # ?

    socket_type = socket.SOCK_DGRAM

    max_packet_size = 8192

    def get_request(self):
        data, client_addr = self.socket.recvfrom(self.max_packet_size)
        return (data, self.socket), client_addr

    def server_activate(self):
        """
        udp没有listen可调

        :return:
        """
        pass

    def shutdown_request(self, request):
        """
        没什么shutdown可调

        :param request:
        :return:
        """
        self.close_request(request)

    def close_request(self, request):
        """
        也不用关闭一个请求

        :param request:
        :return:
        """
        pass


# 下述的mixin改变的是基类中关于process request的改写,也就是获取到一个请求后处理时

class ForkingMixIn:

    timeout = 300
    active_children = None
    max_children = 40

    def collect_children(self):
        """Internal routine to wait for children that have exited."""
        if self.active_children is None:
            return

        # If we're above the max number of children, wait and reap them until
        # we go back below threshold. Note that we use waitpid(-1) below to be
        # able to collect children in size() syscalls instead
        # of size(): the downside is that this might reap children
        # which we didn't spawn, which is why we only resort to this when we're
        # above max_children.
        while len(self.active_children) >= self.max_children:
            try:
                pid, _ = os.waitpid(-1, 0)
                self.active_children.discard(pid)
            except OSError as e:
                if e.errno == errno.ECHILD:
                    # we don't have any children, we're done
                    self.active_children.clear()
                elif e.errno != errno.EINTR:
                    break

        # Now reap all defunct children.
        for pid in self.active_children.copy():
            try:
                pid, _ = os.waitpid(pid, os.WNOHANG)
                # if the child hasn't exited yet, pid will be 0 and ignored by
                # discard() below
                self.active_children.discard(pid)
            except OSError as e:
                if e.errno == errno.ECHILD:
                    # someone else reaped it
                    self.active_children.discard(pid)

    def handle_timeout(self):
        self.collect_children()

    def process_request(self, request, client_address):

        self.collect_children()
        pid = os.fork()
        if pid:
            # 父进程
            if self.active_children is None:
                self.active_children = set()
            self.active_children.add(pid)  # 加入到父进程的活跃子进程集合中
            self.close_request(request)  # 父进程无需再管这个请求了
            return
        else:
            # 子进程
            try:
                #
                self.finish_request(request, client_address)
                self.shutdown_request(request)
                os._exit(0)
            except:
                try:
                    self.handle_error(request, client_address)
                    self.shutdown_request(request)
                finally:
                    os._exit(1)


class ThreadingMixIn:

    daemon_threads = False

    def process_request_thread(self, request, client_address):

        try:
            self.finish_request(request, client_address)
            self.shutdown_request(request)
        except:
            self.handle_error(request, client_address)
            self.shutdown_request(request)

    def process_request(self, request, client_address):

        t = threading.Thread(target=self.process_request_thread, args=(request, client_address))
        t.daemon = self.daemon_threads
        t.start()


class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

if hasattr(socket, 'AF_UNIX'):

    class UnixStreamServer(TCPServer):
        address_family = socket.AF_UNIX

    class UnixDatagramServer(UDPServer):
        address_family = socket.AF_UNIX

    class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass

    class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass


# handler中流程:init setup handle finish
# 获得的参数:request client_address server
class BaseRequestHandler:

    def __init__(self, request, client_address, server):

        self.request = request
        self.client_address = client_address
        self.server = server

        self.setup()
        try:
            self.handle()
        finally:
            self.finish()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass


class StreamRequestHandler(BaseRequestHandler):

    rbufsize = -1
    wbufsize = 0
    timeout = None
    disable_nagle_algorithm = False

    def setup(self):
        """
        连接来的socket

        :return:
        """
        self.connection = self.request

        if self.timeout is not None:
            self.connection.settimeout(self.timeout)
        if self.disable_nagle_algorithm:
            self.connection.setsockopt(socket.IPPROTO_TCP,
                                       socket.TCP_NODELAY, True)

        # socket.makefile
        self.rfile = self.connection.makefile('rb', self.rbufsize)
        self.wfile = self.connection.makefile('wb', self.wbufsize)

    def finish(self):
        """
        结束连接

        :return:
        """
        if not self.wfile.closed:
            try:
                self.wfile.flush()
            except socket.error:
                # An final socket error may have occurred here, such as
                # the local error ECONNABORTED.
                pass
        self.wfile.close()
        self.rfile.close()


class DatagramRequestHandler(BaseRequestHandler):

    def setup(self):
        try:
            from cStringIO import StringIO
        except ImportError:
            from StringIO import StringIO
        self.packet, self.socket = self.request
        self.rfile = StringIO(self.packet)
        self.wfile = StringIO()

    def finish(self):
        self.socket.sendto(self.wfile.getvalue(), self.client_address)

asyncio server

import sys
import loguru
import asyncio
import logging
import uvloop

# 日志基本配置
logging.basicConfig(
    level=logging.DEBUG,
    format='%(name)s: %(message)s',
    stream=sys.stdout,
)
log = logging.getLogger('main')

# 服务基本信息
SERVER_ADDRESS = ('127.0.0.1', 10030)

"""
启动uvloop例例有三种方式
"""

# 使用原生asyncio实例
# event_loop = asyncio.get_event_loop()

# 使用uvloop策略
# asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
#event_loop = asyncio.get_event_loop()

# 创建uvloop实例
loop = uvloop.new_event_loop()
asyncio.set_event_loop(loop)
event_loop = asyncio.get_event_loop()


class EchoServer(asyncio.Protocol):

    def connection_made(self, transport):
        self.transport = transport
        self.address = transport.get_extra_info('peername')
        self.log =logging.getLogger('EchoServer_{}_{}'.format(*self.address))
        self.log.info("connection accepted")

    def data_received(self, data):
        self.log.info('received {!r}'.format(data))
        print(len(data))
        # self.transport.write(data)
        # 回复客户端
        self.transport.write(b"Received success")
        self.log.info('sent {!r}'.format("Received success"))

    def eof_received(self):
        self.log.info("received EOF")
        if self.transport.can_write_eof():
            self.transport.write_eof()

    def connection_lost(self, error):
        if error:
            self.log.error("ERROR: {}".format(error))
        else:
            self.log.debug("closing")
        super().connection_lost(error)


if __name__ == "__main__":
    try:
        factory = event_loop.create_server(EchoServer, *SERVER_ADDRESS)

        server = event_loop.run_until_complete(factory)

        log.info('starting up tcp on {} port {}'.format(*SERVER_ADDRESS))
        event_loop.run_forever()
    finally:
        server.close()
        event_loop.run_until_complete(server.wait_closed())
        event_loop.close()
        log.info("closed event loop")

客户端

import asyncio
from asyncio import AbstractEventLoop

import loguru
import sys
import logging
import functools


MESSAGES = [
    b'This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message .This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message .This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message .This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message .This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the messageThis is the message.This is the message.This is the message.This is the message',
    b'It will be sent ',

]

logging.basicConfig(
    level=logging.DEBUG,
    format='%(name)s: %(message)s',
    stream=sys.stderr,
)
log = logging.getLogger('main')

SERVER_ADDRESS = ('127.0.0.1', 10030)

event_loop: AbstractEventLoop = asyncio.get_event_loop()


class EchoClient(asyncio.Protocol):

    def __init__(self, messages, future):
        super().__init__()
        self.messages = messages
        self.log = logging.getLogger("EchoClient")
        self.f = future

    def connection_made(self, transport):
        self.transport = transport
        self.address = transport.get_extra_info("peername")
        self.log.info('connectiong to {} port {}'.format(*self.address))
        for msg in self.messages:
            transport.write(msg)
            self.log.debug("sending {!r}".format(msg))
        if transport.can_write_eof():
            transport.write_eof()

    def data_received(self, data):
        # self.log.debug("received {!r}".format(data))
        print("received {!r}".format(data))

    def eof_received(self):
        self.log.debug("receive")
        self.transport.close()
        if not self.f.done():
            self.f.set_result(True)

    def connction_lost(self, exec):
        self.log.debug("server closed connection")
        self.transport.close()
        if not self.f.done():
            self.f.set_result(True)
        super().connection_lost(exec)


client_completed = asyncio.Future()

client_factory = functools.partial(
    EchoClient,
    messages=MESSAGES,
    future=client_completed
)

factory_coroutine = event_loop.create_connection(
    client_factory,
    *SERVER_ADDRESS,
)


log.info('waiting for client to complete')

try:
    event_loop.run_until_complete(factory_coroutine)
    event_loop.run_until_complete(client_completed)
finally:
    log.info('closing event loop')
    event_loop.close()


# 普通运行方式2
# import socket
# with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
#     sock.connect(('127.0.0.1', 10030))
#     while 1:
#         data = b"test"
#         sock.send(data)
#         print(sock.recv(1024))

参考:
http://www.cnblogs.com/jokerbj/p/7422349.html

http://xiaorui.cc/2016/03/08/%E8%A7%A3%E5%86%B3golang%E5%BC%80%E5%8F%91socket%E6%9C%8D%E5%8A%A1%E6%97%B6%E7%B2%98%E5%8C%85%E5%8D%8A%E5%8C%85bug/

http://xiaorui.cc/2016/04/15/%E6%89%93%E9%80%A0mvc%E6%A1%86%E6%9E%B6%E4%B9%8Bsocket%E8%8E%B7%E5%8F%96http%E7%B2%98%E5%8C%85%E9%97%AE%E9%A2%98/

https://www.jianshu.com/p/065c53cab328

https://mozillazg.com/2017/08/python-asyncio-note-io-protocol.html#hidid3

你可能感兴趣的:(同步socket, 异步socket, 多线程socket, 多进程socket)