zoukankan      html  css  js  c++  java
  • 练习题|网络编程-socket开发

    原文:https://www.cnblogs.com/shengyang17/p/8822745.html

    1、什么是C/S架构?

      C指的是client(客户端软件),S指的是Server(服务端软件),C/S架构的软件,实现服务端软件与客户端软件基于网络通信。

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

      互联网协议就是计算机界通用的语言;互联网协议分为osi七层或tcp/ip五层或tcp/ip四层;

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

    数据链路层的功能:定义了电信号的分组方式按照以太网协议;一组电信号构成一个数据包,叫做一组数据‘帧’;每一数据帧分成:报头head和数据data两部分。head前六个字节和后六个字节是mac地址,基于mac地址来标示对方;在局域网内以广播的方式工作。

    网络层功能:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址。

    传输层功能:建立端口到端口的通信,端口即应用程序与网卡关联的编号。tcp和udp

    应用层功能:有自己的协议如http、ftp协议,跑应用软件。

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

    tcp协议,客户端给服务端发一次消息,服务端要回应下并且给发给客户端,然后客户端再发给服务端,中间两步回应下+给客户端发消息可以合成一步,链接建立完成也就是三次握手;客户端说要断开链接PIN=1,服务端确认下ack=1,客户端接收到了,这条管道就断开了,服务端要断开发PIN=1,客户端回一个ack=1,管道就断开了。

    客户端说把数据传完了,服务端不一定传完数据了,中间那两步不能合成一步,所以断开链接需要四次挥手。

    4、为何基于tcp协议的通信比基于udp协议的通信更可靠?

       tcp协议一定是先建好双向链接,发一个数据包要得到确认才算发送完成,没有收到就一直给你重发;udp协议没有链接存在,udp直接丢数据,不管你有没有收到。

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

      流式协议指的是tcp协议,数据报协议指的是udp协议

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

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

     服务端先初始化Socket实例化一个类拿到对象(才能调用下面的接口),然后绑定IP端口(bind),监听(listen)就是说客户端可以来连我了,调用accept接收链接;这时客户端初始化一个socket,然后connect与服务端建立好双向链接与accept对应。客户端发送请求数据,服务端处理请求并给客户端回应数据,这样一个通信循环;最后关闭套接字,一次交互结束。

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

     如客户端只recv(1024), 可结果比1024长那怎么办,只好在服务器端的IO缓冲区里把客户端还没收走的暂时存下来,等客户端下次再来收,所以当客户端第2次调用recv(1024)就会首先把上次没收完的数据先收下来,再收df命令的结果。

    这个现象叫做粘包,就是指两次结果粘到一起了。它的发生主要是因为socket缓冲区导致的。

    所谓粘包问题原因:(1)主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。(2)发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

    发生粘包的情况:(1)发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)(2)接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

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

    server

    复制代码
    #Author:Kris
    
    # import socket
    #
    # HOST = ''                 # Symbolic name meaning all available interfaces
    # PORT = 50007              # Arbitrary non-privileged port
    #
    # sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # sock_server.bind((HOST, PORT))
    #
    # sock_server.listen(1) #开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
    # conn, addr = sock_server.accept() #阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
    #
    # with conn:
    #     print('Connected by', addr)
    #     while True:
    #         data = conn.recv(1024) #接收1024个字节
    #         print("server recv:",conn.getpeername(), data.decode())
    #         if not data: break #收不到数据,就break
    #         conn.sendall(data) #把收到的数据再全部返回给客户端
    
    import socket
    
    HOST = ''                 # Symbolic name meaning all available interfaces
    PORT = 50007              # Arbitrary non-privileged port
    
    sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock_server.bind((HOST, PORT))
    
    sock_server.listen(1) #开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
    conn, addr = sock_server.accept() #阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
    
    with conn:
        print('Connected by', addr)
        while True:
            data = conn.recv(1024) #接收1024个字节
            print("recv from Alex:",conn.getpeername(), data.decode())
            if not data: break #收不到数据,就break
    
            response = input(">>>").strip()
            conn.send(response.encode())
            print("send to alex:",response)
    复制代码

    client

    复制代码
    #Author:Kris
    
    import socket
    
    HOST = 'localhost'    # The remote host
    PORT = 50007              # The same port as used by the server
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((HOST, PORT))
    
    while True:
    
        msg = input(">>>:").strip()
        if len(msg) == 0:continue
    
        client.sendall(msg.encode()) #发送用户输入的数据,必须是bytes模式
    
        data = client.recv(1024)
    
        print('Received',data.decode()) #收到服务器的响应后,decode一下
    复制代码

       

    9、基于tcp socket,开发简单的远程命令执行程序,允许用户执行命令,并返回结果

     server

    复制代码
    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',9901)) #0-65535:0-1024给操作系统使用
    phone.listen(5)
    print('starting...')
    while True: # 链接循环
        conn,client_addr=phone.accept()
        print(client_addr)
        while True: #通信循环
            try:
                #1、收命令
                cmd=conn.recv(1024)
                if not cmd:break #适用于linux操作系统
                #2、执行命令,拿到结果
                obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
                stdout=obj.stdout.read()  #它就是bytes格式;正确的
                stderr=obj.stderr.read() #错误的结果
                #3、把命令的结果返回给客户端
                print(len(stdout)+len(stderr))
                conn.send(stdout+stderr) #+是一个可以优化的点,申请一个新的内存空间
    
            except ConnectionResetError: #适用于windows操作系统
                break
        conn.close()
    
    phone.close()
    复制代码

    client

    复制代码
    import socket
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',9901))
    while True:
        #1、发命令
        cmd=input('>>: ').strip() #ls /etc
        if not cmd:continue
        phone.send(cmd.encode('utf-8'))
    
        #2、拿命令的结果,并打印
        data=phone.recv(1024) #1024是一个坑
        print(data.decode('gbk'))
    
    phone.close()
    复制代码

    10、基于tcp协议编写简单FTP程序,实现上传、下载文件功能,并解决粘包问题

     服务端

    复制代码
    import socket
    import struct
    import json
    import subprocess
    import os
    
    class MYTCPServer:
        address_family = socket.AF_INET
    
        socket_type = socket.SOCK_STREAM
    
        allow_reuse_address = False
    
        max_packet_size = 8192
    
        coding='utf-8'
    
        request_queue_size = 5
    
        server_dir='file_upload'
    
        def __init__(self, server_address, bind_and_activate=True):
            """Constructor.  May be extended, do not override."""
            self.server_address=server_address
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
            if bind_and_activate:
                try:
                    self.server_bind()
                    self.server_activate()
                except:
                    self.server_close()
                    raise
    
        def server_bind(self):
            """Called by constructor to bind the socket.
            """
            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):
            """Called by constructor to activate the server.
            """
            self.socket.listen(self.request_queue_size)
    
        def server_close(self):
            """Called to clean-up the server.
            """
            self.socket.close()
    
        def get_request(self):
            """Get the request and client address from the socket.
            """
            return self.socket.accept()
    
        def close_request(self, request):
            """Called to clean up an individual request."""
            request.close()
    
        def run(self):
            while True:
                self.conn,self.client_addr=self.get_request()
                print('from client ',self.client_addr)
                while True:
                    try:
                        head_struct = self.conn.recv(4)
                        if not head_struct:break
    
                        head_len = struct.unpack('i', head_struct)[0]
                        head_json = self.conn.recv(head_len).decode(self.coding)
                        head_dic = json.loads(head_json)
    
                        print(head_dic)
                        #head_dic={'cmd':'put','filename':'a.txt','filesize':123123}
                        cmd=head_dic['cmd']
                        if hasattr(self,cmd):
                            func=getattr(self,cmd)
                            func(head_dic)
                    except Exception:
                        break
    
        def put(self,args):
            file_path=os.path.normpath(os.path.join(
                self.server_dir,
                args['filename']
            ))
    
            filesize=args['filesize']
            recv_size=0
            print('----->',file_path)
            with open(file_path,'wb') as f:
                while recv_size < filesize:
                    recv_data=self.conn.recv(self.max_packet_size)
                    f.write(recv_data)
                    recv_size+=len(recv_data)
                    print('recvsize:%s filesize:%s' %(recv_size,filesize))
    
    
    tcpserver1=MYTCPServer(('127.0.0.1',8080))
    
    tcpserver1.run()
    复制代码

    客户端

    复制代码
    import socket
    import struct
    import json
    import os
    
    
    
    class MYTCPClient:
        address_family = socket.AF_INET
    
        socket_type = socket.SOCK_STREAM
    
        allow_reuse_address = False
    
        max_packet_size = 8192
    
        coding='utf-8'
    
        request_queue_size = 5
    
        def __init__(self, server_address, connect=True):
            self.server_address=server_address
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
            if connect:
                try:
                    self.client_connect()
                except:
                    self.client_close()
                    raise
    
        def client_connect(self):
            self.socket.connect(self.server_address)
    
        def client_close(self):
            self.socket.close()
    
        def run(self):
            while True:
                inp=input(">>: ").strip()
                if not inp:continue
                l=inp.split()
                cmd=l[0]
                if hasattr(self,cmd):
                    func=getattr(self,cmd)
                    func(l)
    
    
        def put(self,args):
            cmd=args[0]
            filename=args[1]
            if not os.path.isfile(filename):
                print('file:%s is not exists' %filename)
                return
            else:
                filesize=os.path.getsize(filename)
    
            head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
            print(head_dic)
            head_json=json.dumps(head_dic)
            head_json_bytes=bytes(head_json,encoding=self.coding)
    
            head_struct=struct.pack('i',len(head_json_bytes))
            self.socket.send(head_struct)
            self.socket.send(head_json_bytes)
            send_size=0
            with open(filename,'rb') as f:
                for line in f:
                    self.socket.send(line)
                    send_size+=len(line)
                    print(send_size)
                else:
                    print('upload successful')
    
    
    client=MYTCPClient(('127.0.0.1',8080))
    
    client.run()
    复制代码

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

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

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

     server

    复制代码
    import socket
    import subprocess
    import time
    
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server.bind(('127.0.0.1', 8080))
    while True:
        data, client_addr = server.recvfrom(1024)
        print(data, client_addr)
        obj = subprocess.Popen(data.decode('utf-8'),shell=True,  # time 命令在windows 下不能用
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        print(stdout+stderr)
        server.sendto(stdout+stderr,client_addr)
        if data.decode('utf-8') == 'time':
            str_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
            # str_time = '2017-01-01 00:00:00'
            server.sendto(str_time.encode('gbk'), client_addr)
    
    server.close()
    复制代码

    client

    复制代码
    import socket
    import os
    import time
    client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    while True:
        msg = input('>>>:').strip()
        client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
        data,server_addr = client.recvfrom(1024)
        print(data.decode('utf-8'),server_addr)
        localtime = time.localtime()
        os.system("date %d-%d-%d" % (localtime.tm_year, localtime.tm_mon, localtime.tm_mday))  # 设置日期
        os.system("time %d:%d:%d.0" % (localtime.tm_hour, localtime.tm_min, localtime.tm_sec))  # 设置时间
    
    client.close()
    复制代码
    分类: 练习
  • 相关阅读:
    Django Rest Framework 视图和路由
    DRF 权限 频率
    DRF 版本 认证
    Serializers 序列化组件
    学DRF之前
    RESTful
    windows下vmware配置nat网络
    python之路——网络编程
    图片上传
    数据库基本设计规范:
  • 原文地址:https://www.cnblogs.com/fmgao-technology/p/9150284.html
Copyright © 2011-2022 走看看