• 23 网络编程--本章练习题


    1、什么是C/S架构

    Client客户端 软件    Server服务端软件

    一个C/S架构就是,实现服务端软件与客户端软件基于网络通信。

    互联网中处处是C/S架构:

    Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

    栗子:

      如12306网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种)

      腾讯作为服务端为你提供视频播放,你下载腾讯视频客户端观看

    C/S架构与socket的关系:

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

    2、互联网协议是什么?分别介绍 五层协议中的每一层的功能?

    互联网协议:世界上所有计算机之间通信的标准协议,就像英语是世界上通用的语言一样

    简单的说,计算机之间的通信标准就是互联网协议

    按功能不同,人们将互联网协议分为osi七层、tcp/ip五层、tcp/ip四层

    osi七层:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

    tcp/ip五层:应用层、传输层、网络层、数据链路层、物理层

    tcp/ip四层:应用层、传输层、网络层、网络接口层

    五层:

    应用层:  软件如:qq,浏览器

    传输层:  建立端口到端口的通信  0-65535 0-1023为系统占用端口     两种协议:tcp ucp

    网络层:  引入一套新的地址来区分不同的广播域/子网,这套地址即网络地址(ip协议)

    数据链路层:  定义了电信号的分组方式  以太网协议(ethernet)  mac地址指网卡地址    广播

    物理层:  主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0

    3、基于TCP协议通信,为何建立连接需要三次握手,而断开连接却需要四次挥手

    建立连接  三次握手:client发送请求建立通道;server收到请求并同意,同时也发送请求建通道;client收到请求并同意,建立完成

    断开连接  四次挥手:client发送请求断开通道;server收到请求并同意,同时还回复client上一条消息;server也发送请求断开通道;client受到消息结束

    4、为何基于TCP协议的通信比基于UDP协议的通信更可靠

    TCP:可靠,对方给了确认收到信息,才发下一个,如果没有收到确认信息就重发

    UDP:不可靠一直发数据,不需要对方回应

    5、流式协议指什么协议,数据报协议指的是什么协议?

    流式协议:TCP协议,可靠传输

    数据报协议:UDP协议,不可靠传输

    也就是TCP和UDP的区别:

        TCP是面向连接的,可靠的字节流服务
        UDP是面向无连接的数据报服务

    6、什么是socket?简述基于tcp协议的套接字通信流程

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/ip

    协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。

     服务端:创建socket对象,绑定ip端口bind(),设置最大链接数listen(),accept()与客户端的connect()创建双向管道
    ,send()   recv()    close()
    客户端:创建socket对象,connect() 与服务端accept()创建双向管道,send()  recv()  close()

    7、什么是粘包?socket中造成粘包的原因是什么?那些情况会发生粘包现象?

    粘包:

    数据粘在一起

    主要因为:接收方不知道消息之间的界限,不知道一次性提取

    多少字节的数据造成的数据量比较小,时间间隔比较短,就合并成了一个包,
    这是底层的一个优化算法(Nagle算法)
    原因:
    1 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会当做一个包发出去,产生粘包)
    服务端:
    from socket import *
    phone = socket(AF_INET,SOCK_STREAM)
    phone.setsockopt(SOL_SOCKET,SOCK_STREAM,1)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    print('start running...')
    
    coon,addr = phone.accept() #等待连接
    
    data1 = coon.recv(10)
    data2 = coon.recv(10)
    
    print('------------>',data1.decode('utf-8'))
    print('------------>',data2.decode('utf-8'))
    coon.close()
    phone.close()
    

    客户端:

    from socket import *
    import time
    phone = socket(AF_INET,SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    
    phone.send('hello'.encode('utf-8'))
    phone.send('helloworld'.encode('utf-8'))
    phone.close()
    2 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

     服务端:

    from socket import *
    phone = socket(AF_INET,SOCK_STREAM)
    phone.setsockopt(SOL_SOCKET,SOCK_STREAM,1)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    print('start running...')
    
    coon,addr = phone.accept() #等待连接
    
    data1 = coon.recv(2) #一次没有接收完整
    data2 = coon.recv(10)  #下一次接收的时候会先取旧的数据,然后取新的
    # data3 = coon.recv(1024)  #接收等5秒后的信息
    print('------------>',data1.decode('utf-8'))
    print('------------>',data2.decode('utf-8'))
    # print('------------>',data3.decode('utf-8'))
    coon.close()
    phone.close()
    

    客户端:

    from socket import *
    import time
    phone = socket(AF_INET,SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    
    phone.send('hello'.encode('utf-8'))
    time.sleep(5)
    phone.send('haiyan'.encode('utf-8'))
    phone.close()

    8、基于Socket开发一个聊天程序,实现两端互相发送和接收消息

    8.1、tcp协议实现

    TCP协议:
    服务端:
    import socket
    server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8081)
    MAX_RECV_SIZE = 1024
    server_socket.bind(ip_port)
    server_socket.listen(5)
    while True :
        conn, client_addr = server_socket.accept()
        while True:
            data = conn.recv(MAX_RECV_SIZE).decode('utf-8')
            print(data)
            msg = input('(q 退出)>>').strip()
            if not  msg:break
            if msg == 'q':
                exit()
            conn.send(msg.encode('utf-8'))
    
    客户端:
    import socket
    client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8081)
    MAX_RECV_SIZE = 1024
    client_socket.connect(ip_port)
    while True:
        msg = input('(q 退出)>>').strip()
        if not msg:continue
        if msg == 'q':
            exit()
        client_socket.send(msg.encode('utf-8'))
        data = client_socket.recv(MAX_RECV_SIZE)
        print(data.decode('utf-8'))
    View Code

    8.2、udp协议实现,客户端:不需要:connect(),服务端不需要:listen()  accept()

    s.recvfrom(1024) 主要用在udp,得到参数为数据和地址,从而可以根据地址返回消息    msg,addr = s.recvfrom(1024)           addr 收到来自发送端的地址

     s.sendto(字节,address)主要用在udp根据地址发送   sendto(msg,addr) addr地址是接收方的地址

    客户端:
    
    import socket
    ip_port = ('127.0.0.1',9000)
    BUFSIZE = 1024
    udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    while True:
        MSG = input('>>').strip()
        udp_client.sendto(MSG.encode('utf-8'),ip_port)
        back_msg,addr = udp_client.recvfrom(BUFSIZE)
        print(back_msg.decode('utf-8'))
    udp_client.close()
    
    服务端:
    
    import socket
    ip_socket =('127.0.0.1',9000)
    BUFSIZE = 1024
    udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    udp_server.bind((ip_socket))
    
    while True:
        msg,addr = udp_server.recvfrom(BUFSIZE)
        print(msg.decode('utf-8'),addr)
        response = input('>>').strip()
        udp_server.sendto(response.encode('utf-8'),addr)
    
    udp_server.close()
    View Code

    9、基于TCP/UDP  SOCKET 开发简单的远程命令执行程序ssm,允许用户执行命令,并返回结果

    TCP服务端:

    import socket
    import struct
    import subprocess
    # subprocess模块来实现对系统命令或脚本的控制
    ip_port = ('127.0.0.1',8080)
    server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server_socket.bind(ip_port)
    server_socket.listen(5)
    while True:
        conn,addr = server_socket.accept()
        while True:
            client_data = conn.recv(1024).decode('utf-8')
            res = subprocess.Popen(client_data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            # stdin标准输入;stdout输出,stderr错误句柄
            stdout = res.stdout.read()
            stderr = res.stderr.read()
            
            # print('stdout', stdout, type(stdout))  # b'' <class 'bytes'> 二进制模式
            # print('stderr', stderr, type(stderr))  #  b'' <class 'bytes'> 二进制模式
    
            # stdin = res.stdin.read()
            # 先发报头 ---- struct
            header = struct.pack('i',len(stdout+stderr))
            conn.send(header)
            conn.send(stdout)
            conn.send(stderr)
        conn.close()
    View Code

    TCP客户端:

    import socket
    import struct
    client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8080)
    client_socket.connect(ip_port)
    while True:
        cmd = input('>>').strip()
        if not cmd:continue
        client_socket.send(cmd.encode('utf-8'))
        head = client_socket.recv(4)
        head_len = struct.unpack('i',head)[0]# 解包返回元祖(a,)
        recv_size = 0
        total_data = b''
        while recv_size < head_len:
            recv_data = client_socket.recv(1024)
            recv_size += len(recv_data)
            total_data += recv_data
        print("返回的消息:%s" % total_data.decode('gbk'))
    View Code

     #----------------------------------------------------------------------------------------------------------

    UDP服务端:

    import subprocess,struct,socket
    server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    ip_port = ('127.0.0.1',8080)
    server.bind(ip_port)
    while True:
        cmd,addr = server.recvfrom(1024)
        res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        stdout = res.stdout.read()
        stderr = res.stderr.read()
        server.sendto(stdout+stderr,addr)
    server.close()
    View Code

    UDP客户端:

    import subprocess,struct,socket
    client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    ip_port = ('127.0.0.1',8080)
    while True:
        cmd = input('>>').strip()
        if not cmd:continue
        client.sendto(cmd.encode('utf-8'),ip_port)
        data,addr = client.recvfrom(1024)
        print(data.decode('gbk'))
    client.close()
    View Code

    10、基于TCP协议编写简单ftp(面向对象)(文件传输协议FTP的传输有两种方式:ASCII、二进制。)程序

    实现上传、下载文件功能,并解决粘包问题

    说明:1、没有考虑断点续传;2、没有考虑,允许上传和下载文件,并保证文件的一致性(md5值)

    服务端:

    import socket,os,sys
    import struct,pickle
    import subprocess
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    sys.path.append(BASE_DIR)
    class ftp_server:
        ip_port = ('127.0.0.1',8080)
        RECV_MAX = 1024
        LISTEN_MAX = 5
        def __init__(self):
            self.server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            self.server_socket.bind(self.ip_port)
            self.server_socket.listen(self.LISTEN_MAX)
        def get(self,conn):
            """
            1 判断文件在服务器是否存在
    
                    1.1存在下载,解决粘包问题
                        1.1.1.得到文件大小(字节为单位)file_size,发送报头给客户端
                    1.2不存在 报错
            """
    
            file_path = os.path.join(BASE_DIR,'db',self.msg[1])
            if os.path.exists(file_path):
                header_dic = {
                    'filename': 111,
                    'file_md5': 111,
                    'file_size': os.path.getsize(file_path)
                }
                header_bytes = pickle.dumps(header_dic)
                # 文件的大小以字节为单位
    
                head_size_len = struct.pack('i',len(header_bytes)) # 打 包成 四个字节
                conn.send(head_size_len)
                conn.send(header_bytes)
                self.handle_file(file_path,conn,1)
            else:
                print('文件不存在')
                head_size = struct.pack('i', 0)  # 打 包成 四个字节
                conn.send(head_size)
    
    
        def put(self,conn):
            """
            0.判断根据文件名判断,服务端是否已存在该文件
            1.存在----返回 0
            2.不存在---返回 1
                2.1.解压 报头 head
    
    
            """
    
            file_path = os.path.join(BASE_DIR, 'db',self.msg[1])
            if os.path.exists(file_path):
                file_exist = struct.pack('i', 0)  # 打 包成 四个字节
                conn.send(file_exist)
            else:
                file_exist = struct.pack('i', 1)  # 打 包成 四个字节
                conn.send(file_exist)
                file_size_len = struct.unpack('i',conn.recv(4))[0]
                # file_size = conn.recv(file_size_len)
                get_size = 0
                with open(file_path,'wb') as f:
                    while True:
                        if get_size < file_size_len:
                            recv_size = conn.recv(1024)
                            f.write(recv_size)
                            get_size += len(recv_size)
                        else:
                            print('下载成功')
                            res = struct.pack('i', 0)  # 打 包成 四个字节
                            conn.send(res)
                            break
    
        @property
        def server_accept(self):
            conn, addr = self.server_socket.accept()
            try:
                self.handle(conn)
            except Exception as e:
                print(e)
                conn.close()
        def handle(self,conn):
            while True:
                try:
                    cmd_data = conn.recv(self.RECV_MAX).decode('utf-8')
                    if not cmd_data:break
                    self.msg = cmd_data.split()
                    cmd = self.msg[0]
    
                    if hasattr(self,cmd):
                        getattr(self,cmd)(conn)
                except Exception as e:
                    print(e)
                    conn.close()
                    break
        def handle_file(self,file_path,conn,num):
            if num == 1:
                with open(file_path,'rb') as f:
                    while True:
                        data = f.read(1024)
                        if data:
                            conn.send(data)
                        else:
                            break
    
    
    if __name__ == '__main__':
        obj = ftp_server()
        obj.server_accept
    View Code

    客户端:

    import socket
    import struct
    import subprocess,os,sys,pickle
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    sys.path.append(BASE_DIR)
    
    class ftp_client:
        ip_port = ('127.0.0.1',8080)
        def __init__(self):
            self.client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        def get(self):
            """"
            0.判断指令是否正确
            1.客户端判断文件是否存在
                    1.1、存在---输出该文件已存在
                    1.2、不存在:执行下载功能
                        1.2.1.解压,服务端传来的文件报头长度-file_size_len,
                            1.2.1.1.如果file_size_len==0,需要下载的文件不存在
                            1.2.1.2.file_size_len!=0, handle_file进行写入文件
                            1.2.1.3.接受文件大小file_size
            """
            if len(self.cmd)>1:
                file_path = os.path.join(BASE_DIR,self.cmd[1])
                if os.path.exists(file_path):
                    print('----该文件已存在----')
                else:
                    file_size_len = struct.unpack('i',self.client_socket.recv(4))[0] #数据的总长度,以字节为单位
                    if file_size_len == 0:
                        print('----需要下载的文件不存在----')
                    #接收数据
                    else:
                        recv_size = 0
                        header_bytes = self.client_socket.recv(file_size_len) # 字节大小
                        head_dic = pickle.loads(header_bytes)
                        file_size = head_dic['file_size']
    
                        self.handle_file(file_path,file_size)
            else:
                print('没有输入文件名')
    
    
    
        def put(self):
            """
            0.判断指令是否正确
            1.判断文件是否存在
                1.客户端存在
                    1.2.判断服务端是否已存在该文件
                        1.2.1.存在  服务器返回------服务端已存在该文件----
                        1.2.2.不存在 发送报头的长度,再发送报头
    
            """
            if len(self.cmd)>1:
                file_path = os.path.join(BASE_DIR, self.cmd[1])
    
                if os.path.exists(file_path):
                    # file_name = struct.pack('i',self.cmd[1]) # 打包成了字节模式
                    # self.client_socket.send(file_name)
                    """判断服务端是否存在该文件"""
                    file_exist=struct.unpack('i',self.client_socket.recv(4))[0]
                    if file_exist == 0:
                        print('----服务端已存在该文件----')
    
                    else:
                        self.handle_file(file_path,1)
                        #上传的结果反馈
                        res = struct.unpack('i',self.client_socket.recv(4))[0]
                        if res == 0:
                            print('put sucess')
                else:
                    print('该文件不存在')
    
            else:
                print('没有输入文件名')
        @property
        def handle(self):
            while True:
                msg = input('>>').strip()
                if not msg:continue
                self.client_socket.send(msg.encode('utf-8')) #发送完整的指令给服务端口
                self.cmd = msg.split()
                if hasattr(self,self.cmd[0]):
                    getattr(self,self.cmd[0])()
    
    
    
        def handle_file(self,file_path,file_size):
            if self.cmd[0] == 'get':
    
                with open(file_path,'wb') as f:
                    get_size = 0
                    while True:
                        if get_size < file_size:
                            file_bytes = self.client_socket.recv(1024)# 收到的是字节
                            f.write(file_bytes)
                            get_size += len(file_bytes)
                            # self.progress_bar(1, get_size, file_size)
                        else:
                            print('下载成功')
                            break
            if self.cmd[0] == 'put':
                file_size2 = os.path.getsize(file_path)# 文件的大小本质上就是指字节的长度
                # head_dic间接封装报头的长度
                # head_dic={'filename':self.cmd[1],
                #           'file_md5':'aaaa',
                #           'file_size':file_size2
                #           }
                # head_pickle = pickle.dumps(head_dic)
    
                file_bytes = struct.pack('i', file_size2)#直接封装报头的长度
                self.client_socket.send(file_bytes) # 发送报头长度
                with open(file_path,'rb') as f:
                    while True:
                        data = f.read(1024)
                        if data:
                            self.client_socket.send(data)
                        else:
                            break
        @property
        def connect(self):
            self.client_socket.connect(self.ip_port)
            self.handle
    
        def progress_bar(self,num,get_size, file_size):
            pass
    
    if __name__ == '__main__':
        obj = ftp_client()
        obj.connect
    View Code

    11、基于UUP协议编写程序,实现功能

    (1)执行指定的指令,让客户端可以查看服务端的时间

    (2)执行指定的指令,让客户端可以与服务端的时间同步

    服务端:

    # UDP协议的两个主要方法sendto和recvfrom详解
    # TypeError: sendto() takes 2 or 3 arguments (1 given)
    import socket
    import os,json,time
    ip_port = ('127.0.0.1',8081)
    server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    server.bind(ip_port)
    # udp 不需要 监听listen ,不需要接收 accept
    while True:
        msg,addr= server.recvfrom(1024)
        if msg.decode('utf-8') == 'time':
            server_time = time.strftime("%Y-%m-%d %X",time.localtime())
            server.sendto(server_time.encode('utf-8'),addr)
            # print(server_time)
    server.close()
    View Code

     客户端:

    import socket
    import time
    import json
    ip_port = ('127.0.0.1',8081)
    client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    while True:
        cmd = input('>>').strip()
        if not cmd:continue
        client.sendto(cmd.encode('utf-8'),ip_port)
        server_time,addr = client.recvfrom(1024)
    
        print('服务端的时间',server_time.decode('utf-8'))
    View Code
  • 相关阅读:
    Prism-超轻量的开源框架
    1的数目
    二叉树中和为某一值得路径
    把二叉树打印成多行
    对称的二叉树
    二叉树的下一个节点
    删除链表中重复的结点
    数组中的重复数字
    连表中环入口的节点
    把字符串换成整数
  • 原文地址:https://www.cnblogs.com/foremostxl/p/9664782.html
Copyright © 2020-2023  润新知