zoukankan      html  css  js  c++  java
  • python ==》 网络编程

     一、服务端和客户端

    BS架构 (腾讯通软件:server+client)

    CS架构 (web网站)

    C/S架构与socket的关系:

    我们学习socket就是为了完成C/S架构的开发

    二、OSI七层模型

    互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层

    学习socket一定要先学习互联网协议:

    1.首先:本节课程的目标就是教会你如何基于socket编程,来开发一款自己的C/S架构软件

    2.其次:C/S架构的软件(软件属于应用层)是基于网络进行通信的

    3.然后:网络的核心即一堆协议,协议即标准,你想开发一款基于网络通信的软件,就必须遵循这些标准。

    4.最后:就让我们从这些标准开始研究,开启我们的socket编程之旅

    socket:就是位于 应用层和传输层 之间。socket帮我们封装了一系列协议,统一标准。

    三、socket是什么?

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

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

    四、套接字发展史及分类

    套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。 

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

    套接字家族的名字:AF_UNIX

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

    2、基于网络类型的套接字家族

    套接字家族的名字:AF_INET

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

    五、套接字工作流程

          生活中的场景,你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。    

    生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

    socket例子:

    1.服务端与客户端的正常通信。

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    print('really ==== go!!')
    
    conn,client_addr=phone.accept()
    print(conn,client_addr)
    
    data=conn.recv(1024)
    conn.send(data.upper())
    print('client data:<%s>'%data)
    
    conn.close()
    phone.close()
    服务端
    import socket
    
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect  (('127.0.0.1',8080))
    
    phone.send('hello'.encode('utf-8'))
    data1 = phone.recv(1024)
    print('server back res:<%s>'%data1)
    
    phone.close()
    客户端
    首先:
    服务端 先开始运行,等待接收,
    之后,客户端运行,向服务端发送信息。
    
    结果如下:
    服务端:
    really ==== go!!
    <socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53789)> ('127.0.0.1', 53789)
    client data:<b'hello'>
    
    客户端:
    server back res:<b'HELLO'>
    
    客户端发了 hello  给服务端, 服务端收到信息,做了 大写化处理,返回给客户端。

    2.服务端与客户端的正常通信。socket通信循环

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    print('really ==== go!!')
    
    conn,client_addr=phone.accept()
    print(conn,client_addr)
    
    while True:  #通信循环
        data=conn.recv(1024)
        # print('server has recv')
        conn.send(data.upper())
        print('client data:<%s>'%data)
    
    conn.close()
    phone.close()
    服务端
    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     #如果cmd为空,继续发
        phone.send(cmd.encode('utf-8'))
        print('====> has send')
        data = phone.recv(1024)
        print('server back res:<%s>'%data)
    
    phone.close()
    客户端
    首先:
    这里比上一个例子,优化了,这里设置了input,可以自己输入。
    添加了个循环,当客户端输入为空,不在报错,而是需要继续输入。
    
    结果如下:
    服务端:
    really ==== go!!
    <socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53865)> ('127.0.0.1', 53865)
    client data:<b'1'>
    client data:<b'2'>
    client data:<b'3'>
    client data:<b'a'>
    client data:<b'b'>
    client data:<b'c'>
    
    客户端:
    >>:1
    ====> has send
    server back res:<b'1'>
    >>:2
    ====> has send
    server back res:<b'2'>
    >>:3
    ====> has send
    server back res:<b'3'>
    >>:a
    ====> has send
    server back res:<b'A'>
    >>:b
    ====> has send
    server back res:<b'B'>
    >>:c
    ====> has send
    server back res:<b'C'>
    
    注意:
    这里服务端在接到客户端的额信息是,只做了加大化吃力,所以把abc处理后为ABC返回给客户端。

    3.服务端与客户端的正常通信。socket链接循环。

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    print('really ==== go!!')
    while True:  #链接循环
        conn,client_addr=phone.accept()
        print(conn,client_addr)
    
        while True:  #通信循环
            try:
                data=conn.recv(1024)
                # print('server has recv')
                conn.send(data.upper())
                print('client data:<%s>'%data)
            except Exception:
                break
        conn.close()
    phone.close()
    服务端
    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     #如果cmd为空,继续发
        phone.send(cmd.encode('utf-8'))
        data = phone.recv(1024)
        print('server back res:<%s>'%data)
    phone.close()
    客户端1
    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     #如果cmd为空,继续发
        phone.send(cmd.encode('utf-8'))
        print('====> has send')
        data = phone.recv(1024)
        print('server back res:<%s>'%data)
    
    phone.close()
    客户端2
    首先,这里加的链接循环是为了防止,当我们有多个客户端时,
    要关闭其中一个,而不导致整个程序出错。
    在没做链接循环前,当我们关闭了其中一个客户端,服务端那里是不能在运行的。
    
    输出结果:
    服务端:
    really ==== go!!
    <socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53898)> ('127.0.0.1', 53898)
    client data:<b'1'>
    client data:<b'2'>
    client data:<b'a'>
    client data:<b'b'>
    client data:<b'c'>
    <socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53911)> ('127.0.0.1', 53911)
    <socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53911)> ('127.0.0.1', 53911)
    client data:<b'a'>
    client data:<b's'>
    client data:<b'd'>
    
    客户端1:
    >>:1
    server back res:<b'1'>
    >>:2
    server back res:<b'2'>
    >>:3
    server back res:<b'3'>
    >>:a
    server back res:<b'A'>
    >>:b
    server back res:<b'B'>
    >>:c
    server back res:<b'C'>
    
    
    客户端2:
    >>:a
    ====> has send
    server back res:<b'A'>
    >>:s
    ====> has send
    server back res:<b'S'>
    >>:d
    ====> has send
    server back res:<b'D'>

    4.socket模拟ssh远程执行。

    import subprocess
    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8081))
    phone.listen(5)
    print('really ==== go!!')
    while True:  #链接循环
        conn,client_addr=phone.accept()
        print(conn,client_addr)
    
        while True:  #通信循环
            try:
                cmd=conn.recv(1080)
                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()
                conn.send(stdout+stderr)
            except Exception:
                break
        conn.close()
    phone.close()
    服务端
    import socket
    
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect  (('127.0.0.1',8081))
    
    while True:
        cmd = input('>>:').strip()
        if not cmd:continue     #如果cmd为空,继续发
        phone.send(cmd.encode('utf-8'))
        cmd_res = phone.recv(1080)
        print(cmd_res.decode('gbk'))
    phone.close()
    客户端
    说明:
    1.客户端远程执行服务端。
    2.登录的是windows系统,用的是‘gbk’ 编码。
    
    
    服务端:
    really ==== go!!
    <socket.socket fd=400, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8081), raddr=('127.0.0.1', 53955)> ('127.0.0.1', 53955)
    
    
    客户端:
    >>:dir
     驱动器 E 中的卷没有标签。
     卷的序列号是 0001-0682
    
     E:zbkwork_work_8.21 socket模拟ssh远程执行 的目录
    
    2017/08/21  19:25    <DIR>          .
    2017/08/21  19:25    <DIR>          ..
    2017/08/21  19:25               333 客户端1.py
    2017/08/21  16:29               367 客户端2.py
    2017/08/21  19:25               966 服务端2.py
    2017/08/21  19:05               413 模块subprocess.py
                   4 个文件          2,079 字节
                   2 个目录 266,249,355,264 可用字节
    
    >>:ipconfig /all
    
    Windows IP 配置
    
       主机名  . . . . . . . . . . . . . : DESKTOP-0QR7V9H
       主 DNS 后缀 . . . . . . . . . . . : 
       节点类型  . . . . . . . . . . . . : 混合
       IP 路由已启用 . . . . . . . . . . : 否
       WINS 代理已启用 . . . . . . . . . : 否
    
    以太网适配器 以太网:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开连接
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller
       物理地址. . . . . . . . . . . . . : 80-FA-5B-3C-8F-54
       DHCP 已启用 . . . . . . . . . . . : 是
       自动配置已启用. . . . . . . . . . : 是
    
    无线局域网适配器 本地连接* 1:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开连接
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter
       物理地址. . . . . . . . . . . . . : 70-1C-E7-32-BC-D5
       DHCP 已启用 . . . . . . . . . . . : 是
       自动配置已启用. . . . . . . . . . : 是
    
    无线局域网适配器 WLAN:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Intel(R) Dual Band Wireless-
    >>:
    输出结果

    5.socket  解决粘包问题。

    import struct
    import subprocess
    from socket import *
    
    phone = socket(AF_INET,SOCK_STREAM)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    print('ready go !!')
    
    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 = stdout.subprocess.PIPE,
                                                stderr = 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:
                break
        conn.close()
    phone.close()
    服务端
    import struct
    from socket import *
    
    phone = socket(AF_INET,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_struct = phone.recv(4)
        unpack_res = struct.unpack('i',header_struct)
        total_size = unpack_res[0]
        total_data = b''
        recv_size = 0
        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()
    客户端

     6.json 解决粘包问题。

    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()
    客户端
  • 相关阅读:
    IOS开发之WIFI及IP相关
    Git使用教程
    git push到GitHub的时候遇到! [rejected] master -> master (non-fast-forward)的问题 【转载】
    Linux高并发web服务器开发 【转载】
    Linux下open函数、read函数、write函数 【转载】
    Linux epoll 详解 【转载】
    Linux Socket 编程简介 【转载】
    信号量sem_init,sem_wait,sem_post
    linux线程之pthread_join和pthread_detach 【转载】
    C++ static类成员,static类成员函数 【转载】
  • 原文地址:https://www.cnblogs.com/zhongbokun/p/7414205.html
Copyright © 2011-2022 走看看