Java教程

第八周整理笔记

本文主要是介绍第八周整理笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录
  • 一、软件开发架构
    • 1.简介
    • 2.常见软件开发架构
      • 2.1.c/s架构
      • 2.2.b/s架构
    • 3.架构优劣势
    • 4.架构发展趋势
  • 二、网络编程
    • 1.简介
      • 1.1.网络编程目的
      • 1.2.网络编程意义
      • 1.3.网络编程起源
    • 2.网络编程要求
  • 三、OSI七层协议
    • 1.应用层
    • 2.表示层
    • 3.会话层
    • 4.传输层
      • 4.1.PORT协议
      • 4.2.IP+PORT
      • 4.3.TCP协议与UDP协议
        • 4.3.1.TCP协议
        • 4.3.2.UDP协议
    • 5.网络层
      • 5.1.IP协议
      • 5.2.IP地址
      • 5.3.IP特征
    • 6.数据链路层
    • 7.物理连接层
  • 四、网络相关名词
  • 五、socket套接字
    • 1.基于文件类型的套接字家族
    • 2.基于网络类型的套接字家族
    • 3.socket模块之服务端
    • 4.socket模块之客户端
  • 六代码优化
    • 1.send与recv
    • 2.消息自定义
    • 3.循环通信
    • 4.服务端能够持续提供服务
    • 5.消息不能为空
    • 6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)
    • 7.客户端异常退出会发送空消息(针对mac、linux)
  • 七、半连接池
  • 八、黏包问题
    • 1.TCP特性
    • 2.recv
    • 3.struct模块
    • 4.解决黏包问题的终极方案:
      • 4.1.服务端
      • 4.2.客户端

一、软件开发架构

1.简介

编写项目之前需要遵循的代码层面上的规范(代码运行的流程、环节、步骤)

2.常见软件开发架构

2.1.c/s架构

客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的

c/s架构 描述 例子
c(client) 客户端 即将去消费的客人
s(server) 服务端 提供服务的商店

客户端:泛指客户端应用程序(app软件),程序需要先安装才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大

服务端具备的特征:

  1. 24小时不间断提供服务
  2. 固定地址(不能经常改变)
  3. 可以同一时间服务很多人

2.2.b/s架构

浏览器端与服务器端架构,这种架构是从用户层面来划分,通过浏览器充当各个服务端的客户端,用于想要体验服务不需要下载指定的客户端

本质:c/s架构

b/s架构 描述
b(broswer) 浏览器
s(server) 服务器

3.架构优劣势

c/s架构 b/s架构
优势 下载对应的客户端可以在客户端软件内高度定制相关服务 不需要下载客户端,可以快速体验服务
优势 使用前必须先下载客户端,比较繁琐 定制花里胡哨的功能较为繁琐

4.架构发展趋势

  • 统一接口原则(方便用户使用,可以给自己圈用户便于后期收割)
  • c/s架构和b/s架构交错使用(避免各自劣势)

二、网络编程

1.简介

基于互联网编写代码,程序可以实现远程数据交互

1.1.网络编程目的

网络编程的本质是为了解决计算机之间的远程交互

1.2.网络编程意义

学习完网络编程之后,可以编写一个c/s架构的软件

1.3.网络编程起源

任何先进的技术一般都源于军事,网络编程由美国军方开发

没有网络编程的时候,如果两台计算机之间要交互数据只能用硬盘拷贝

2.网络编程要求

计算机之间要想实现远程数据交互,首要条件就是要有物理连接介质

例:

物品 物理连接介质
早期电话 电话线
大屁股电脑 网线
笔记本电脑 网卡

三、OSI七层协议

规定了计算机涉及到数据远程交互时必须要经过的部件或流程(每一层都有各自的功能和规范),所有的计算机在涉及到网络传输必须要有相同的零部件

  • 数据发送出去时,从上往下走
  • 数据接收回来时,从下往上走

image

1.应用层

​ 主要取决于程序员自己采用什么策略和协议

​ 常见的协议:HTTP、HTTPS、FTP…

2.表示层

3.会话层

4.传输层

4.1.PORT协议

​ 端口协议:规定了一台计算机上的每一个正在运行的应用程序都必须有一个端口号,端口号相当于是计算机用来管理多个应用程序的标记

​ 端口号特征:

  1. 端口号范围:0-65535

    端口号范围 使用
    0-1024 一般是操作系统内部需要使用
    1024-8000 一般是常见的软件已经使用了
    8000+ 平时写代码使用8000之后的端口号
  2. 端口号动态分配

  3. 同一时间同一台计算机端口号不能冲突

4.2.IP+PORT

名字 描述
IP 用于标识全世界任意一台接入互联网的计算机
PORT 用于标识一台计算机上的某个应用程序
IP+PORT 用于标识全世界任意一台接入互联网的计算机上的某一个具体的程序

4.3.TCP协议与UDP协议

​ 规定了数据传输所遵循的规则

协议 描述
TCP 类似于打电话:你一句我一句,有来有往
UDP 似于发短信:只要发送了就什么都不管了
4.3.1.TCP协议

三次握手:建立双向通道,同时让大量的客户端朝服务端发送建立TCP连接的请求

image

四次挥手:断开双向通道,中间两步不能合并(需要有检查的时间)

image

  • 优势:
  1. 基于TCP传输数据非常安全,因为有双向通道
  2. 基于TCP传输数据数据不容易丢失,因为有二次确认机制
  • 劣势:每次发送数据都需要返回确认消息,否则在一定的时间会反复发送
4.3.2.UDP协议
  • 优势:基于UDP协议发送数据,没有任何的通道也没有任何限制

  • 劣势:UDP发送数据没有TCP安全,因为没有二次确认机制

5.网络层

5.1.IP协议

规定了任何接入互联网的计算机都必须有一个IP地址

5.2.IP地址

  • IPV4:点分十进制
    • 最小:0.0.0.0
    • 最大:255.255.255.255
    • 随着社会的发展,同时能够上网的人越来越多,上述地址不够分
  • IPV6:能够给地球上每一粒沙分一个IP地址

5.3.IP特征

每个IP都自带定位(IP代理)

6.数据链路层

  1. 规定了电信号的分组方式
  2. 规定了每台计算机必须都有一块网卡
  3. 网卡上必须有一串记录:电脑的以太网地址(类似于人的身份证号)/mac地址
    • 前6位:产商编号
    • 后六位:生产流水线号

eg:可以根据mac地址查找计算机(基于mac地址实现)

7.物理连接层

保证物理连接介质的条件,传递电信号(主要研究插网线情况)

四、网络相关名词

名词 描述
交换机 能够让接入交换机的多台计算机实现彼此互联
以太网通信(mac通信) 有了交换机之后,根据电脑的mac地址就可以实现数据交互
局域网 有某个固定区域组成的网络
广域网 可以看成是更大区域的局域网
路由器 将多个局域网连接到一起的设备
网址(URL) 统一资源定位符,本质:IP+PORT(ip:port)

五、socket套接字

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

套接字家族名字:AF_UNIX

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

套接字家族名字:AF_INET

3.socket模块之服务端

import socket
 
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
sock, address = server.accept()
print(sock, address)  # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~')  # 朝客户端发送数据
data = sock.recv(1024)  # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close()  # 断链接
server.close()  # 关机

4.socket模块之客户端

import socket
 
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024)  # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server')  # 朝服务端发送数据
# 4.关闭
client.close()

六代码优化

1.send与recv

客户端与服务端不能同时执行同一个,不能同时收或发

  • 有一个收,另外一个就是发
  • 有一个发,另外一个就是收

2.消息自定义

input获取用户数据(主要是编码解吗)

3.循环通信

给数据交互环节添加循环

4.服务端能够持续提供服务

需求:不会因为客户端断开连接而报错

方法:异常捕获

  • 一旦客户端断开连接,服务端结束通信循环调到连接处等待

5.消息不能为空

判断是否为空,是则重新输入(主要针对客户端)

6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)

from socket import SOL_SOCKET, SO_REUSEADDR
server.aetsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 加在bind前面

7.客户端异常退出会发送空消息(针对mac、linux)

针对接收的消息加判断处理即可

七、半连接池

主要为了做缓冲,避免太多的无效等待

sever.listen(5)

八、黏包问题

# 服务端
import socket
 
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
server.listen(5)
 
sock, address = server.accept()
 
print(sock.recv(5))
print(sock.recv(5))
print(sock.recv(5))


# 客户端
import socket
 
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
 
 
client.send(b'jason')
client.send(b'kevin')
client.send(b'jerry')

1.TCP特性

流式协议:所有的数据类似于水流连接在一起

2.recv

不知道即将要接收的数据量多大,如果知道的话不会产生黏包

3.struct模块

struct模块无论数据长度是多少,都可以打包成固定长度,然后基于该固定长度可以反向解析出真实长度

注意:struct模块针对数据量特别大的数字没有办法打包

import struct

info = 'something to send'
print(len(info))  # 17 数据原本的长度
res = struct.pack('i', len(info))  # 将数据原本的长度打包
print(len(res))  # 打包之后的长度是4
ret = struct.unpack('i', res)  # 将打包之后固定长度为4的数据拆包
print(ret[0])  # 13 又得到了原本数据的长度

思路:

发送 接收
1.先将真实数据的长度制作成固定长度 4
2.先发送固定长度的报头
3.再发送真实数据
1.先接收固定长度的报头 4
2.再根据报头解压出真实长度
3.根据真实长度接收

4.解决黏包问题的终极方案:

4.1.服务端

  1. 先构造一个数据的详细字典
  2. 对字典数据进行打包处理,得到一个固定长度的数据 4
  3. 将上述打包之后的数据发送给客户端
  4. 将字典数据发送给客户端
  5. 将真实数据发送给客户端
import socket
import os
import struct
import json
 
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
 
while True:
    sock, address = server.accept()
    while True:
        # 1.先构造数据文件的字典
        file_dict = {
            'file_name': 'Jason合集.txt',
            'file_size': os.path.getsize(r'../XXX视频合集.txt'),
            'file_desc': '内容很精彩 一定不要错过',
            'file_root': 'jason'
        }
        # 2.将字典打包成固定长度的数据
        dict_json = json.dumps(file_dict)
        file_bytes_dict = len(dict_json.encode('utf8'))
        dict_len = struct.pack('i', file_bytes_dict)
        # 3.发送固定长度的字典报头
        sock.send(dict_len)
        # 4.发送真实字典数据
        sock.send(dict_json.encode('utf8'))
        # 5.发送真实数据
        with open(r'../XXX视频合集.txt', 'rb') as f:
            for line in f:
                sock.send(line)
        break

4.2.客户端

  1. 先接收固定长度的数据 4
  2. 根据固定长度解析出即将要接收的字典真实长度
  3. 接收字典数据
  4. 根据字典数据获取真实数据长度
  5. 接收真实数据长度
import socket
import struct
import json
 
client = socket.socket()
client.connect(('127.0.0.1', 8080))
 
while True:
    # 1.先接收长度为4的报头数据
    header_len = client.recv(4)
    # 2.根据报头解包出字典的长度
    dict_len = struct.unpack('i', header_len)[0]
    # 3.直接接收字典数据
    dict_data = client.recv(dict_len)  # b'{"file_name":123123123}'
    # 4.解码并反序列化出字典
    real_dict = json.loads(dict_data)
    print(real_dict)
    # 5.从数据字典中获取真实数据的各项信息
    total_size = real_dict.get('file_size')  # 32423423423
    file_size = 0
    with open(r'%s' % real_dict.get('file_name'), 'wb') as f:
        while file_size < total_size:
            data = client.recv(1024)
            f.write(data)
            file_size += len(data)
        print('文件接收完毕')
        break
这篇关于第八周整理笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!