• socket编程


    一、CS架构

    server端要求:

    1、力求一直提供服务

    2、一个server端socket绑定到一个唯一的IP+端口地址,多个客户端发起connect各带一个conn套接字通道 去连接服务端。

    二、socket

    socket就是为了完成C/S架构软件的开发,但是如果是C/S架构的软件就一定需要解决双方通信问题。

    基于网络通信就需要了解复杂的网络协议 TCP/IP协议,于是socket出现了;

    1、什么是sock?

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

    所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的

    2、socket分类

    (1)基于文件类型的套接字家族

    套接字家族的名字:AF_UNIX

    unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信(Linux操作系统)

    (2)基于网络类型的套接字家族   AF_INET

    (还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)(ssh,nginx,mysql)

     

    3、soket套接字的工作流程

    服务端:得到Socket对象,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。

    客户端:客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。

    C/S交换阶段:客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

    服务端套接字函数
    s.bind() 绑定(主机,端口号)到套接字
    s.listen() 开始TCP监听
    s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

    客户端套接字函数
    s.connect() 主动初始化TCP服务器连接
    s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

    4、基于TCP协议的套接字

    (1)实现一个简单的套接字

    import socket
    phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建一个基于网络的(socket.AF_INET)tcp协议传输的sock(socket.SOCK_STREAM)
    phon.bind(("127.0.0.1",8090)) #把创建好的套接字,绑定在 唯一的IP和端口上。
    phon.listen(80)            #开始监听
    conn,addr=phon.accept()   #等待客户端通过TCP三次握手发起 连接  (把某个连接,和连接的对象分解赋值给 conn,和addr)
    data=conn.recv(1024)      #接收 连接进来的某一个客户端,发送过来的消息
    print("接受到来自客户端发来的消息",data)
    conn.send(data.upper())    #发送接受的消息 给某一个客户端

    执行结果

    服务端:

    客户端:

    (2)循环通信的套接字

    虽然上述已经实现了一个简单的套接字,但服务端的 socket 只能接受 1个客户端的连接,1次通信后就结束了。

    所以可以利用while..Ture构建一个通信循环,实现1个客户端可以和服务端一直通信(循环通信)

    服务端:

    import socket
    phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建一个基于网络的(socket.AF_INET)tcp协议传输的sock(socket.SOCK_STREAM
    phon.bind(("127.0.0.1",8090)) #把创建好的套接字,绑定在 唯一的IP和端口上。
    phon.listen(80)                 #开始监听
    conn,addr=phon.accept()   #等待客户端通过TCP三次握手发起 连接  (把某个连接,和连接的对象分解赋值给 conn,和addr)
    while True:              #循环通信----------------------------------------------------------------------->
        data=conn.recv(1024)      #接收 连接进来的某一个客户端,发送过来的消息
        print("接受到来自客户端发来的消息",data)
        conn.send(data.upper())    #发送接受的消息 给某一个客户端
    conn.close()
    phon.close()

    客户端:

    import socket
    phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phon.connect(('127.0.0.1',8090))     #客户端的phon.connect正好对于服务端的phon1.accept()
    while True: #循环通信----------------------------------------------------------------------------------->>
        mes=input('---->: '.strip())
        if not mes:
            continue
        res=phon.send(mes.encode('utf-8'))  #发消息
        data=phon.recv(1024)             #收消息
        print(data.decode('gbk'))
    
    phon.close()

     3、循环连接的套接字

    由于1客户端连接到服务端就会建立一个连接通道(电话线),但是如果客户端关闭了这个通道,服务端的conn.recv(1024)方法无法接收到客户端的数据。

    在Windows平:台sock服务端就会报错,抛出异常;

    在Linux平台:服务端循环收空数据,陷入死循环;

    解决方法:建连接循环,捕捉到客户端关闭了连接通道,服务端继续accept()等待下一次连接

    tcp服务端

    import socket
    import subprocess
    iphon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #(建立一个socket对象)
    iphon.bind(('127.0.0.1',8080))  #绑定到 IP+端口上 成为唯一的socket
    iphon.listen(5)                   #设置连接池的个数
    print('starting........')
    while True:  #连接循环------------------------------------------------------------------>
        conn,addr=iphon.accept()  #等待电话连接
        print('电话线路是',conn)
        print('客户手机号:',addr)
        while True: #通信循环 发送和接收
            try:
                data=conn.recv(1024) #接受消息 最大从内存里接受1024MB数据
                print('客户端发来的消息是%s'%data)
                data=data.decode('utf-8') #从客户端发来的数据是经过编码的字节数据 所以需要解码 成Unicode
                res=subprocess.Popen( data, shell=True, stdout=subprocess.PIPE)  #解码后传进subprocess.Popen去cmd执行
                data1 = res.stdout.read().decode('gbk')      #cmd是gbk编码所以需要gbk解码
                print(data1)                                  #打印解码后的结果
                data1=data1.encode('gbk')                   #编码
                conn.send(data1)  #发送消息                  #编码后再传输给客户端
            except Exception:
                break
        conn.close()
    iphon.close()
    #

    TCP客户端

    import socket
    phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phon.connect(('127.0.0.1',8080))     #客户端的phon.connect正好对于服务端的phon1.accept()
    while True: #循环通信
        mes=input('---->: '.strip())
        if not mes:
            continue
        res=phon.send(mes.encode('utf-8'))  #发消息
        data=phon.recv(1024)             #收消息
        print(data.decode('gbk'))
    
    
    phon.close()

    4、由于客户端连接服务端断开后,仍然存在四次挥手的time_wait状态在占用地址(所以 服务器高并发情况下会有大量的time_wait状态的需要优化)

    import socket
    phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建一个基于网络的(socket.AF_INET)tcp协议传输的sock(socket.SOCK_STREAM)
    phon.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #处理客户端连接断开,后的time_out------------------------->
    phon.bind(("127.0.0.1",8090)) #把创建好的套接字,绑定在 唯一的IP和端口上。
    phon.listen(80)                 #开始监听
    conn,addr=phon.accept()   #等待客户端通过TCP三次握手发起 连接  (把某个连接,和连接的对象分解赋值给 conn,和addr)
    while True:              #循环通信
        data=conn.recv(1024)      #接收 连接进来的某一个客户端,发送过来的消息
        print("接受到来自客户端发来的消息",data)
        conn.send(data.upper())    #发送接受的消息 给某一个客户端
    conn.close()
    phon.close()

    5、1个服务端套接字 和 多个客户端 套接字通信(socketserver模块实现并发 

    循环通信:解决了客户端和服务端可以持续发消息。

    连接循环:解决了客户端 突然断开和服务端建立的通道,服务端无法继续和这个关闭了这个通道通信,抛异常退出的问题;

    但此时的服务端套接字 还只能和1客户端建立通信管道,无法同时接收 多个客户端套接字的连接,实现并发

    socketserve模块执行流程:

    #1、接收多个连接:一个客户端连接建立之后,服务端开一个socketserver.ThreadingTCPServe线程 实例化出一个FTP类的对象。
    #2、自动执行Ftp对象handle方法
    #3、自动执行对象 handle方法,进入通信循环,self.request就是这个连接客户端的套接字对象。

    import  socketserver                      #socketserver()模块基于 线程实现并发
    
    class FTP(socketserver.BaseRequestHandler):#继承socketserver.BaseRequestHandler类
        def handle(self):                      #2、自动执行对象 handle方法,进入通信循环,
                                               #3 self.request就是这个连接客户端的套接字对象
    
            print('==========>')
            while True:                      #4、进入循环通信
                data=self.request.recv(1024)  #接收数据方法:self.request.recv(1024)
                print(data)
                self.request.send(data)       #发送数据方法:self.request.send(data)
    
    if __name__ == '__main__':
        obj=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FTP)#1、接受多个连接:一个客户端连接建立之后,服务端开一个socketserver.ThreadingTCPServe线程 实                                                                  例化出一个FTP对象
        obj.serve_forever()
        obj.serve_forever()                   #等于accpet()永远接收连接


  • 相关阅读:
    LR杂记-nmon+analyser监控linux系统资源
    accept函数
    android performClick使用
    #line 的作用是改变当前行数和文件名称
    C++常用排序法、随机数
    C语言运算符优先级及结合性
    如何高效把一字节的位对换, bit0和bit7,bit1和bit6,以此类推.
    NumPy
    Rational Rose、PowerDesign、Visio的一些比较
    vld,Bounds Checker,memwatch,mtrace,valgrind,debug_new几种内存泄露检测工具的比较,Valgrind Cheatsheet
  • 原文地址:https://www.cnblogs.com/sss4/p/6803466.html
Copyright © 2020-2023  润新知