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
原文地址:https://www.cnblogs.com/foremostxl/p/9664782.html