• 面向对象&网络编程


    1 接口与归一化设计

    1.1 归一化概念:

      归一化的好处:

      1.归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大降低了使用者的使用难度。

      2.归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合

      继承的两种用途

      一:继承基类的方法,并且做出自己改变或者扩展(代码重用):实践中,继承的这种用途意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。

      二:声明某个子类兼容于某基类,定义一个接口类(模仿java的Interface),接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

    复制代码
    class Interface:
        '''
        定义接口Interface类来模仿接口的概念,
        python中压根就没有interface关键字来定义一个接口。
        '''
        def read(self): # 定接口函数read
            pass
    
        def write(self): # 定义接口函数write
            pass
    
    class Txt(Interface): # 文本,具体实现read和write
        def read(self):
            print('文本数据的读取方法')
    
        def write(self):
            print('文本数据的读取方法')
    
    class Sata(Interface): #磁盘,具体实现read和write
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的读取方法')
    
    class Process(Interface):
        def read(self):
            print('进程数据的读取方法')
    
        def write(self):
            print('进程数据的读取方法')
    
    t = Txt()
    s = Sata()
    p = Process()
    
    t.read()    # 运行结果:文本数据的读取方法
    s.read()    # 运行结果:硬盘数据的读取方法
    p.read()    # 运行结果:进程数据的读取方法
    复制代码

    1.2 抽象类

      了解知识点

      与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

      借助abc模块,进行模拟抽象类

    复制代码
    import abc
    class Interface(metaclass=abc.ABCMeta):
        '''
        定义接口Interface类来模仿接口的概念,
        python中压根就没有interface关键字来定义一个接口。
        '''
        @abc.abstractmethod
        def read(self): # 定接口函数read
            pass
        @abc.abstractmethod
        def write(self): # 定义接口函数write
            pass
    
    class Txt(Interface): # 文本,具体实现read和write
        def read(self):
            pass
    
        def write(self):
            pass
    
    t = Txt()
    复制代码

    1.3 多态

      面向对象的多态、多态性

      多态:指的是同一种事物的多种形态

      动物有多种形态:人、狗、猪

    复制代码
    # 多态:同一种事物的多种形态
    class Animal: # 同一类事物:动物
        def talk(self):
            pass
    
    class People(Animal): # 动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal): # 动物的形态之二:狗
        def talk(self):
            print('say wangwang')
    
    p=People()
    d=Dog()
    p.talk()
    d.talk()
    复制代码

    1.4 多态性

      多态性:可以在不考虑实例类型的前提下使用实例

    复制代码
    # 多态:同一种事物的多种形态
    class Animal: # 同一类事物:动物
        def talk(self):
            pass
    
    class People(Animal): # 动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal): # 动物的形态之二:狗
        def talk(self):
            print('say wangwang')
    
    # 多态性:可以在不考虑实例类型的前提下使用实例
    p=People()
    d=Dog()
    
    def Talk(animal):
        animal.talk()
    
    Talk(p)
    Talk(d)
    复制代码

    1.5 多态性的好处

      多态性的好处:

      1.增加了程序的灵活性

      2.增加了程序可扩展性

    1.6 鸭子类型

      Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

      python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象

      也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

      利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法

    复制代码
    # 二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
    class TxtFile:
        def read(self):
            pass
    
        def write(self):
            pass
    
    class DiskFile:
        def read(self):
            pass
        def write(self):
            pass
    复制代码

    2 封装

    2.1 隐藏

      在python中用双下划线开头的方式将属性隐藏起来

    复制代码
    class Foo:
        __N = 1111 # _Foo__N
        def __init__(self,name):
            self.__Name=name # self._Foo__Name=name
    
        def __f1(self): # _Foo__f1
            print('f1')
        def f2(self):
            self.__f1() # self._Foo__f1()
    
    f = Foo('egon')
    # print(f.__N)   # 把数据类型隐藏起来了
    
    # 这种隐藏需要注意的问题:
    # 1:这种隐藏只是一种语法上变形操作,并不会将属性真正隐藏起来
    print(Foo.__dict__)
    print(f.__dict__)    # 运行结果:{'_Foo__Name': 'egon'}
    print(f._Foo__Name)  # 运行结果:egon
    print(f._Foo__N)     # 运行结果:1111
    
    # 2:这种语法级别的变形,是在类定义阶段发生的,并且只在类定义阶段发生
    Foo.__x = 123123
    print(Foo.__dict__)  # 运行结果:...'__doc__': None, '__x': 123123}
    print(Foo.__x)       # 运行结果:123123
    f.__x = 123123123
    print(f.__dict__)    # 运行结果:{'_Foo__Name': 'egon', '__x': 123123123}
    print(f.__x)         # 运行结果:123123123
    
    # 3:在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,
    # 而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,
    
    # 子类是无法覆盖的。
    class Foo:
        def __f1(self): # _Foo__f1
            print('Foo.f1')
    
        def f2(self):
            self.__f1() # self._Foo_f1
    
    class Bar(Foo):
        def __f1(self): # _Bar__f1
            print('Bar.f1')
    
    b = Bar()
    b.f2()    # 运行结果:Foo.f1
    复制代码

    2.2 封装数据属性

    复制代码
    # 封装不是单纯意义的隐藏
    # 1:封装数据属性:将属性隐藏起来,然后对外提供访问属性的接口,
    # 关键是我们在接口内定制一些控制逻辑从而严格控制使用对数据属性的使用
    class People:
        def __init__(self,name,age):
            if not isinstance(name,str):
                raise TypeError('%s must be str' %name)
            if not isinstance(age,int):
                raise TypeError('%s must be int' %age)
            self.__Name=name
            self.__Age=age
        def tell_info(self):
            print('<名字:%s 年龄:%s>' %(self.__Name,self.__Age))
    
        def set_info(self,x,y):
            if not isinstance(x,str):
                raise TypeError('%s must be str' %x)
            if not isinstance(y,int):
                raise TypeError('%s must be int' %y)
            self.__Name=x
            self.__Age=y
    
    p=People('egon',18)
    p.tell_info()       # 运行结果:<名字:egon 年龄:18>
    
    p.set_info('Egon',19)
    p.tell_info()       # 运行结果:<名字:egon 年龄:19>
    复制代码

    2.3 封装函数属性

      封装函数属性的目的:隔离复杂度

    复制代码
    # 2:封装函数属性:为了隔离复杂度
    # 取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    # 对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    # 隔离了复杂度,同时也提升了安全性
    
    class ATM:
        def __card(self):
            print('插卡')
        def __auth(self):
            print('用户认证')
        def __input(self):
            print('输入取款金额')
        def __print_bill(self):
            print('打印账单')
        def __take_money(self):
            print('取款')
    
        def withdraw(self):
            self.__card()
            self.__auth()
            self.__input()
            self.__print_bill()
            self.__take_money()
    
    a = ATM()
    a.withdraw()
    # 运行结果:
    #       插卡
    #       用户认证
    #       输入取款金额
    #       打印账单
    #       取款
    复制代码

    2.4 静态属性

    复制代码
    class Foo:
        @property
        def f1(self):
             print('Foo.f1')
    
    f = Foo()
    f.f1        # 运行结果:Foo.f1
    复制代码

      示例:计算BMI指数

    复制代码
    '''
    例:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,
    如果我们将其做成一个属性,更便于理解)
    成人的BMI数值:
    过轻:低于18.5
    正常:18.5-23.9
    过重:24-27
    肥胖:28-32
    非常肥胖, 高于32
      体质指数(BMI)=体重(kg)÷身高^2(m)
      EX:70kg÷(1.75×1.75)=22.86
    '''
    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
        @property
        def bmi(self):
            return self.weight / (self.height**2)
    
    p=People('jack',75,1.80)
    p.height=1.86
    print(p.bmi)
    复制代码

    3 面向对象高级

    3.1 反射

      通过字符串,反射到真实的属性上,找到真实的属性

    复制代码
    class Foo:
        x=1
        def __init__(self,name):
            self.name = name
    
        def f1(self):
            print('from f1')
    
    print(Foo.x) # Foo.__dict__['x']
    
    f = Foo('egon')
    print(f.__dict__)
    
    print(f.name)
    print(f.__dict__['name'])
    
    # hasattr
    print(hasattr(f,'name'))    # f.name
    print(hasattr(f,'f1'))      # f.f1
    print(hasattr(f,'x'))       # f.x
    
    # setattr
    setattr(f,'age',18)         # f.age=18
    
    # getattr
    print(getattr(f,'name'))      # f.name
    print(getattr(f,'abc',None))  # f.abc
    print(getattr(f,'name',None)) # f.abc
    
    func = getattr(f,'f1')  # f.f1
    func()
    
    # delattr
    delattr(f,'name')    # del f.name
    print(f.__dict__)
    复制代码

    3.2 item系列

      Item系类,主要包括:__getitem__、__setitem__、 __delitem__,通过这几个item,可以像操作字典一样,操作对象的属性。

    复制代码
    class Foo:
        def __getitem__(self, item):
            print('=====>get')
            return self.__dict__[item]
    
        def __setitem__(self, key, value):
            self.__dict__[key]=value
            # setattr(self,key,value)
    
        def __delitem__(self, key):
            self.__dict__.pop(key)
    
    f = Foo()
    f['name'] = "jack"   # 设置
    print(f["name"])      # 取出属性
    del f["name"]        # 删除
    print(f.__dict__)
    复制代码

    3.3 __str__

      通过__str__,打印对象信息

    复制代码
    class People:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def __str__(self): # 在对象被打印时触发执行
            return '<name:%s age:%s sex:%s>' %(self.name,self.age,self.sex)
    
    p=People('alex',38,'male')
    print(p)    # 运行结果:<name:alex age:38 sex:male>
    复制代码

    3.4 析构方法

      用法:清理一些资源,清理一些python解析器不能清理的资源,例如:清理操作系统的资源,打开文件,向操作系统发起调用,关闭文件句柄资源

    复制代码
    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __del__(self): # 在对象资源被释放时触发
            print('-----del------')
    
    f = Foo("mary")
    del f
    print('对象被释放')
    复制代码

    4 异常

    复制代码
    # 逻辑错误
    # TypeError
    for i in 3:
        pass
    
    # NameError
    aaaaa
    
    # ValueError
    int('asdfsadf')
    
    # IndexError
    l=[1,2]
    l[1000]
    
    #KeyError
    d = {'a':1}
    d['b']
    
    # AttributeError
    class Foo:pass
    Foo.x
    复制代码

    4.1 异常处理

      如果错误发生的条件可预知的,我们需要用if进行处理,在错误发生之前进行预防

      如果错误发生的条件时不可预知的,则需要用到try…except,在错误发生之后进行处理

    复制代码
    #基本语法为
    try:
        # 被检测的代码块
    except 异常类型:
        # try中一旦检测到异常,就执行这个位置的逻辑
    # 示例
    try:
        f=open('a.txt')
        g=(line.strip() for line in f)
        print(next(g))
        print(next(g))
    except StopIteration:
        f.close()
    复制代码

    4.2 多分支异常

    复制代码
    try:
        aaaa
        print('====>1')
        l = []
        l[3]
        print('====>2')
        d = {}
        d['x']
        print('====>3')
    except NameError as e:
        print(e)
    except IndexError as e:
        print(e)
    except KeyError as e:
        print(e)
    复制代码

    4.3 万能异常

    复制代码
    try:
        # aaaa
        print('====>1')
        l = []
        l[3]
        print('====>2')
    except Exception as e:
        print(e)
    复制代码

    4.4 基本结构

    复制代码
    try:
        aaaa
        print('====>1')
        l=[]
        l[3]
        print('====>2')
        d={}
        d['x']
        print('====>3')
    except NameError as e:
        print(e)
    except IndexError as e:
        print(e)
    except KeyError as e:
        print(e)
    except Exception as e:
        print(e)
    else:
        print('在没有错误的时候执行')
    finally:
        print('无论有无错误,都会执行')
    复制代码

    4.5 自定义异常

    复制代码
    class EgonException(BaseException):
        def __init__(self, msg):
            self.msg=msg
        def __str__(self):
            return '<%s>' %self.msg
    
    raise EgonException('jack 的异常')
    复制代码

    5 Socket编程

      IP + 端口,标识唯一一个软件、应用

      tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端

    5.1 简单套接字

    5.1.1 服务端

    复制代码
    import socket
    
    # 买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 插卡
    phone.bind(("127.0.0.1", 8080))
    
    # 开机
    phone.listen(5)
    
    # 等待电话
    print("server start...")
    conn,client_addr = phone.accept()  # tcp链接,client_addr
    print("链接", conn)
    print(client_addr)
    
    # 基于建立的链接,收发消息
    client_data = conn.recv(1024)
    print("客户端的消息", client_data)
    conn.send(client_data.upper())
    
    # 挂电话链接
    conn.close()
    
    # 关机
    phone.close()
    复制代码

    5.1.2 客户端

    复制代码
    import socket
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(("127.0.0.1", 8080))
    
    phone.send("hello".encode("utf-8"))
    server_data = phone.recv(1024)
    print("服务端回应的消息", server_data)
    phone.close() 
    复制代码

    5.2 加上通信循环

    5.2.1 服务端

    复制代码
    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    phone.bind(("127.0.0.1", 8080))
    phone.listen(5)
    print("server start...")
    conn,client_addr = phone.accept()
    
    while True: # 通讯循环
        client_data = conn.recv(1024)
        print("客户端的消息", client_data)
        conn.send(client_data.upper())
    
    conn.close()
    phone.close()
    复制代码

    5.2.2 客户端

    复制代码
    import socket
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(("127.0.0.1", 8080))
    
    while True:
        msg = input(">>").strip()
        if not msg:continue
        phone.send(msg.encode("utf-8"))
        server_data = phone.recv(1024)
        print("server back:", server_data.decode("utf-8"))
    
    phone.close()
    复制代码

    5.3 加上链接循环

    5.3.1 服务端

    复制代码
    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    phone.bind(("127.0.0.1", 8080))
    phone.listen(5)
    print("server start...")
    
    while True: # 链接循环
        conn,client_addr = phone.accept()
        while True: # 通讯循环
            try:
                client_data = conn.recv(1024)
                if not client_data:break # 针对linux系统
                conn.send(client_data.upper())
            except Exception: # 针对windows
                break
        conn.close()
    
    phone.close()
    复制代码

    5.3.2 客户端

    复制代码
    import socket
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    
    while True:
        msg=input('>>: ').strip()
        if not msg:continue
        phone.send(msg.encode('utf-8'))
        server_data = phone.recv(1024)
        print(server_data.decode('utf-8'))
    
    phone.close()
    复制代码

    5.4 模拟ssh远程执行命令

    5.4.1 服务端

    复制代码
    import socket
    import subprocess
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    phone.bind(("127.0.0.1", 8080))
    phone.listen(5)
    print("server start...")
    
    while True: # 链接循环
        conn,client_addr = phone.accept()
        while True: # 通讯循环
            try:
                cmd = conn.recv(1024)
                if not cmd:break # 针对linux系统
    
                # 执行命令,拿到结果
                res = subprocess.Popen(cmd.decode("utf-8"),
                                       shell = True,
                                       stdout = subprocess.PIPE,
                                       stderr = subprocess.PIPE)
                stdout = res.stdout.read()
                stderr = res.stderr.read()
                conn.send(stdout+stderr)
            except Exception: # 针对windows
                break
        conn.close()
    
    phone.close()
    复制代码

    5.4.2 客户端

    复制代码
    import socket
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    
    while True:
        cmd = input('>>').strip()
        if not cmd:continue
        # 发命令
        phone.send(cmd.encode('utf-8'))
    
        # 收命令的执行结果
        cmd_res = phone.recv(1024)
    
        # 打印结果
        print(cmd_res.decode('gbk'))
    
    phone.close()
    复制代码

    5.5 粘包现象

      Tcp出现粘包现象,udp不会出现粘包;主要是tcp基于流式的,像水流一样,没有开头,没有结尾,源源不断,会发生粘包;udp,是数据报的,不会出现粘包。

    5.5.1 服务端

    复制代码
    from socket import *
    s = socket(AF_INET, SOCK_STREAM)
    s.bind(("127.0.0.1", 8080))
    s.listen(5)
    
    conn,addr = s.accept()
    
    # 收发消息
    data1 = conn.recv(1024)
    print("data1:", data1)
    
    data2 = conn.recv(1024)
    print("data2:", data2)
    
    conn.close()
    s.close()
    复制代码

    5.5.2 客户端

    复制代码
    from socket import *
    c = socket(AF_INET, SOCK_STREAM)
    c.connect(("127.0.0.1", 8080))
    
    c.send("hello".encode("utf-8"))
    c.send("world".encode("utf-8"))
    c.close()
    复制代码

    5.6 ssh远程执行命令+定制报头

    5.6.1 服务端

    复制代码
    import socket
    import struct
    import subprocess
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    phone.bind(("127.0.0.1", 8080))
    phone.listen(5)
    print("server start...")
    while True: # 链接循环
        conn,client_addr = phone.accept()
        print(conn, client_addr)
    
        while True: # 通讯循环
            try:
                cmd = conn.recv(1024)
                if not cmd:break
    
                # 执行命令,拿到结果
                res = subprocess.Popen(cmd.decode("utf-8"),
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
                stdout = res.stdout.read()
                stderr = res.stderr.read()
    
                # 制作报头
                header = struct.pack("i", len(stdout)+len(stderr))
    
                # 先发报头(固定长度)
                conn.send(header)
                # 再发真实数据
                conn.send(stdout)
                conn.send(stderr)
            except Exception: # 针对windows
                break
        conn.close()
    
    phone.close()
    复制代码

    5.6.2 客户端

    复制代码
    import socket
    import struct
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(("127.0.0.1", 8080))
    
    while True:
        cmd = input(">>").strip()
        if not cmd:continue
        # 发命令
        phone.send(cmd.encode("utf-8"))
    
        # 先收报头
        header = phone.recv(4)
        total_size = struct.unpack("i", header)[0]
    
        # 再收命令的执行结果
        recv_size = 0
        data = b""
        while recv_size < total_size:
            recv_data = phone.recv(1024)
            recv_size += len(recv_data)
            data += recv_data
    
        # 打印结果
        print(data.decode("gbk"))
    
    phone.close()
    复制代码

    5.7 定制报头的正确方式

    5.7.1 服务端

    复制代码
    import socket
    import struct
    import subprocess
    import json
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    phone.bind(("127.0.0.1", 8080))
    phone.listen(5)
    print("server start...")
    while True: # 链接循环
        conn,client_addr = phone.accept()
        print(conn, client_addr)
    
        while True: # 通讯循环
            try:
                cmd = conn.recv(1024)
                if not cmd:break
    
                # 执行命令,拿到结果
                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), "md5":None}
                header_json = json.dumps(header_dic)
                header_bytes = header_json.encode("utf-8")
    
                # 1.先发报头的长度(固定4个bytes)
                conn.send(struct.pack("i", len(header_bytes)))
    
                # 2.再发报头
                conn.send(header_bytes)
    
                # 3.最后发真实数据
                conn.send(stdout)
                conn.send(stderr)
            except Exception: # 针对windows
                break
        conn.close()
    
    phone.close()
    复制代码

    5.7.2 客户端

    复制代码
    import socket
    import struct
    import json
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(("127.0.0.1", 8080))
    
    while True:
        cmd = input(">>").strip()
        if not cmd:continue
        # 发命令
        phone.send(cmd.encode("utf-8"))
    
        # 先收报头的长度
        struct_res = phone.recv(4)
        header_size = struct.unpack("i", struct_res)[0]
    
        # 再收报头
        header_bytes = phone.recv(header_size)
        print(header_bytes)
        head_json = header_bytes.decode("utf-8")
        head_dic = json.loads(head_json)
    
        total_size = head_dic["total_size"]
    
        # 再收命令的执行结果
        recv_size = 0
        data = b""
        while recv_size < total_size:
            recv_data = phone.recv(1024)
            recv_size += len(recv_data)
            data += recv_data
    
        # 打印结果
        print(data.decode("gbk"))
    
    phone.close()
    复制代码

    5.8 服务端实现并发

    5.8.1 服务端

    复制代码
    import socketserver
    
    class MyTcphandler(socketserver.BaseRequestHandler):
        def handle(self):
            while True: # 通信循环
                print(self.request)
                data = self.request.recv(1024)
                self.request.send(data.upper())
    
    if __name__ == '__main__':
        # 取代链接循环
        server = socketserver.ThreadingTCPServer(("127.0.0.1",8080), MyTcphandler)
        server.serve_forever()
    复制代码

    5.8.2 客户端

    复制代码
    import socket
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(("127.0.0.1", 8080))
    
    while True:
        msg = input(">>").strip()
        if not msg:continue
        phone.send(msg.encode("utf-8"))
        server_data = phone.recv(1024)
        print(server_data.decode("utf-8"))
    phone.close()
    复制代码
  • 相关阅读:
    NSObject-拷贝 NSCopy-NSMutablecopy
    NSObject 排序
    iOS UIView上添加mp4视频
    OC语言中BOOL 和 bool 区别
    便利初始化浅析
    可变字符与不可不可变字符串的心得
    博客开通随笔
    OC总结第五讲:多态
    oc总结第四讲:属性
    oc总结第三讲 setter getter 方法 点运算符
  • 原文地址:https://www.cnblogs.com/ldq1996/p/7688553.html
Copyright © 2020-2023  润新知