1017 笔记

1.软件开发架构:

开发软件,必须要开发一套客户端与服务端

客户端与服务端的作用:

服务端:统一管理数据库的主机中的软件,就叫服务端

客户端:用户安装的软件,向服务端进行请求

c/s架构

Client:客户端 Server:服务端

用户安装客户端,厂商部署服务端

  • 数据存放服务端与客户端的利与弊
服务端:统一处理有更好的安全性和稳定性而且升级比较容易,不过服务器负担增加了
客户端:非常快,将负担分配到每个用户,节约服务器资源,如果本地保存密码账号,安全性较低,且升级比较麻烦

c/s架构的软件:电脑上的QQ,pycharm等,手机端的微信,王者等

b/s架构

Browser:浏览器(客户端) Server:服务端

优点:以浏览器充当客户端,无需用户下载多个软件,也无需用户下载更新软件版本,直接在浏览器上访问需要使用的软件
缺点:消耗网络资源过大,当网络不稳定时,软件的使用也会不稳定

b/s架构的软件:网页上需要输入域名的网址(京东,淘宝)

web浏览器与服务器之间得以相互通信,依靠的是两个协议

第一个是TCP/IP协议(传输层),决定了建立连接的方式和数据如何在网络中传输

第二个是HTTP协议(应用层超文本协议),主要用于在网络上传输HTML格式的文本。

2.网络编程

互联网的组成

边缘部分: 客户端/服务端,这些普通的计算机,负责接收/发送数据

核心部分:传输网络的设备,路由,基站,负责数据的传输

OSI七层协议

互联网的本质就是一系列的网络协议,这个协议就叫OSI协议(一系列协议)根据具体作用不同,由下至上人为划分为七层:

1.物理层

计算机之间传递信息是电信号(0 1),即网络设备之间物理连接的介质,以及网络设备与网络终端之间数据传递规则为物理层,以比特为单位传递

2.数据链路层

数据的传递是比特,需要进行分组组合成字节的形式,称之为数据帧.

以太网协议

1.规定电信号数据的分组方式,组成数据报
2.每一台连接网线的电脑都必须要有一块网卡
	网卡由不同厂商生产
		每块网卡都会有世界上独一无二的12位编号,Mac地址
			前6位:厂商号
			后6位:流水号
	交换机:可以让多台电脑连接到一起(局域网)
	基于以太网协议发送数据
		特点: 广播,单播
		弊端: 广播风暴,不能跨局域网通信
		

同一局域网内通信

将 自己的Mac地址 与 目标对象的Mac地址 与 信息 封装打包广播出去,两个地址做为数据包的头部,与发送的信息构成了数据帧.
广播出去,所有人都会拆包读取地址,接受者地址不是自己就丢弃,是则以数据帧的方式继续广播出去

3.网络层

不在局域网内的电脑间通信,寻找最佳路径传输(引入网络地址),单位数据包,路由器的功能就是选合适的地址.

IP协议

IP地址: 用于标识唯一的一台计算机的(局域网)地址
	IP:点分十进制
		最小值:0.0.0.0
		最大值:255.255.255.25
	IPv4协议版本与IPv6协议版本
	
	本机IP: 回环地址 127.0.0.1 --> localhost

不同局域网的计算机通信

网络层定义IP协议,互联网由一个个彼此隔离的小的局域网组成,局域网的连接由交换机聚合.不同局域网的计算机通信可以
用IP地址标识你在哪个局域网(交换机),Mac地址则是标识你在局域网的位置.
Mac地址及IP地址唯一标识了你在互联网中的位置。

4.传输层

传输层的功能:建立端口到端口的通信

port: 端口用于确认计算机上的一个应用软件。

网络层的IP帮我们区分子网,以太网层的Mac帮我们找到主机,应用程序的寻找则依靠端口,一个端口号对应一个应用程序

TCP/UDP协议

基于端口工作,端口号:标识电脑上的某一个软件,端口号范围:0-65535

注意:操作系统中,一般0-1024的端口都被默认使用了(0-1024不要动)
尽量使用8000之后的端口号

开发中常用软件的默认端口号:

mysql: 3306
mongodb: 27017
Django: 8000
Tomcat: 8080
Flask: 5000
Redis: 6379

若想服务端与客户端进行通信,必须建立连接,产生两条单向通道

一条客户端往服务端发送消息的
另一条是服务端往客户端发送的

TCP协议的工作原理

TCP是一个流式协议

三次握手,四次挥手

1.三次握手建连接
-建立双向通道,建立好连接。
    - listen: 监听
    - established: 确认请求建立连接
- 发送数据:
    write
    read
    客户端往服务端发送数据,数据存放在内存中,需要服务端确认收到,数据才会在内存中释放掉。

    否则,会隔一段时间发送一次,让服务端返回确认收到。
    在一段时间内,若服务端还是不返回确认收到,则取消发送。并释放内存中的数据。

img

  • 第一次握手:客户端给服务器发送一个 SYN 报文。
  • 第二次握手:服务器收到 SYN 报文之后,会应答一个 SYN+ACK 报文。
  • 第三次握手:客户端收到 SYN+ACK 报文之后,会回应一个 ACK 报文。
  • 服务器收到 ACK 报文之后,三次握手建立完成。

为什么只有三次握手才能确认双方接收发送正常

第一次握手:客户端发送网络包,服务端收到了。
	这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。
	这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。
        不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。
	这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
因此,需要三次握手才能确认双方的接收与发送能力是否正常。
三次握手的作用
  • 确认双方的接受能力、发送能力是否正常。
  • 指定自己的初始化序列号,为后面的可靠传送做准备。
  • 如果是 HTTPS 协议的话,三次握手这个过程,还会进行数字证书的验证以及加密密钥的生成。
2.四次挥手断连接

img

刚开始双方都处于 establised 状态,假如是客户端先发起关闭请求,则:

- 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
- 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
- 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
- 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。

需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态
- 服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
time_wait

为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭。

要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。
至于 TIME_WAIT 持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功,就是 ACK 报文,此时处于 CLOSED 状态。

5.会话层

信息传递给对方计算机某些特定的程序,会话层用来区分不同的进程.(tips:如打开两个浏览器,输入两个地址,确认输入的地址和响应的内容在自己想要的浏览器上显示, netstat -n 可以查看会话 ,established为建立连接)

6.表示层

对传输数据进行解密压缩等,针对不同类型数据进行格式描述。

(tips:如压缩加密图片后再传输,如微信界面发送信息再传输过程中加密再解密等,开发人员来进行加密 。)

7.应用层

用户使用的都是应用程序,均工作于应用层,互联网是开放的,大家都可以开发自己的应用程序,数据多种多样,必须规定好数据的组织形式

应用层功能:规定应用程序的数据格式。

  • 例:TCP协议可以为各种各样的程序传递数据,比如Email、WWW、FTP等等。那么,必须有不同协议规定电子邮件、网页、FTP数据的格式,这些应用程序协议就构成了”应用层”。

3.Socket

我们可以利用IP地址+协议+端口号唯一标示网络中的一个进程。能够唯一标示网络中的进程后,它们就可以利用Socket进行通信了

1.什么是Socket

socket是一个模块,可以写一套C/S架构的套接字。

我们经常把Socket翻译为套接字,Socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。

2.为什么要使用Socket

socket套接字会封装好各层协议的工作。
好处:    可以节省开发成本。

Socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件

3.如何使用

import Socket

注意: 客户端与服务端必须尊循:
一端send,另一端recv    
不同两端同时send或recv

4.socket对象方法

1.服务器套接字

1.s.bind()

绑定地址到套接字,以元组(IP+端口号)的形式表示地址

2.s.listen()

开始TCP监听 , 括号内参数指定在拒绝连接之前,等待连接数,一般为5

2.客户端套接字

1.s.connect()

主动初始化TCP服务器连接,一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

2.s.connect()

connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

3.公共用途的套接字

1.s.recv()

接受TCP数据 括号内指定接受的数值字节大小

2.s.send()

发送TCP数据 数据形式以byte形式,需encode转码

3.s.close()

关闭套接字

4.代码

1.socket简单使用

'''server服务端'''
import  socket
# 1.获得socket对象server,默认指定TCP协议
server = socket.socket()

# 2.传入服务端的(IP + 端口) 
server.bind(
    ('127.0.0.1',8848)   #(用户回环地址,自定义端口值)
)

# 3.开始监听
server.listen(5)  # listen(5) 半连接池

# 4.监听是否发送消息,并查看客户端的地址
    # conn :服务端至客户端的管道 addr:客户端的地址
conn,addr= server.accept()
# print(addr)

# 5.服务端接受客户端消息,需解码(设定可以接受字节大小数据)
data = conn.recv(1024).decode('utf-8')
print(data)

# 6.服务端通过单向管道向客户端发送消息
conn.send(('我也好').encode('utf-8'))

# 7.服务端通道关闭
conn.close()

# 8.关闭服务器
server.close()

'''
('127.0.0.1', 49941)
你好'''
'''client客户端'''
import socket
# 1.获得socket对象client
client = socket.socket()

# 2.寻找服务端地址 (服务端的IP + 端口号)
    # client:相当于客户端至服务端的单向通道
client.connect(
    ('127.0.0.1',8848)   # (IP + port )寻找服务端
)

# 3.客户端向服务端发送消息(客户端主动)
client.send(('你好').encode('utf-8'))

# 4.客户端接受服务端的消息,设定接受数据字节大小
data = client.recv(1024).decode('utf-8')
print(data)

# 5.客户端主动断开连接
client.close()

'''我也好'''

2.socket循环通信套接字

'''server服务端'''
import  socket
# 1.获得socket对象server,默认指定TCP协议
server = socket.socket()

# 2.传入服务端的(IP + 端口)
server.bind(
    ('127.0.0.1',8848)   #(用户回环地址,自定义端口值)
)

# 3.开始监听
server.listen(5)  # listen(5) 半连接池

# 4.监听是否发送消息,并查看客户端的地址
    # conn :服务至客户的管道 addr:客户端的地址
conn,addr= server.accept()
print(addr)

# 5.打印时进行循环
while True:

    # 服务端接受客户端消息,需解码(设定可以接受字节大小数据)
    data = conn.recv(1024).decode('utf-8')
    print(data)
    # 判断客户端发送的是否为q 就退出
    if data == 'q':
        break

    # 6.服务端通过单向管道向客户端发送消息,自定义人工输入
    say = input('请输入向客户端发送的消息:').encode('utf-8')
    conn.send(say)

# 7.服务端通道关闭
conn.close()

'''client客户端'''
import socket
# 1.获得socket对象client
client = socket.socket()

# 2.寻找服务端地址 (服务端的IP + 端口号)
    # client:相当于客户端至服务端的单向通道
client.connect(
    ('127.0.0.1',8848)   # (IP + port )寻找服务端
)

# 3.发送消息时进行循环
while True:
    # 自定义输入消息
    msg = input('请输入客户端向服务端发送的消息:')

    # 4.客户端向服务端发送消息(客户端主动)
    client.send(msg.encode('utf-8'))

    # 判断输入为q,则主动退出
    if msg == 'q':
        break

    # 5.不是q,则客户端接受服务端的消息,设定接受数据字节大小
    data = client.recv(1024).decode('utf-8')
    print(data)

# 6.客户端主动断开连接
client.close()

3.循环接受客户端

import socket
# 1获取对象
s = socket.socket()
# 2 地标
s.bind(
    ('127.0.0.1',8848)
)
# 3.监听
s.listen(5)
while True:
    # 4.建立管道
    conn, addr = s.accept()
    # 5.接受信息
    while True:
        try:
            data = conn.recv(1024).decode('utf-8')
            print(data)
            # 判断bug
            if len(data) == 0:
                continue
            if data == 'q':
                break
            # 6.发送信息
            conn.send(data.encode('utf-8'))
        except Exception as e:
                print(e)
                break
    # 7.关闭通道
    conn.close()
import socket
# 1.对象
c = socket.socket()
# 2.获取地址,建立通道
c.connect(
    ('127.0.0.1',8848)
)
while True:
    # 3.发送信息
    msg = input('客户端到用户端:')
    c.send(msg.encode('utf-8'))
    if msg =='q':
        break
    # 4.接收信息
    data = c.recv(1024).decode('utf-8')
    print(data)

c.close()
posted @ 2019-10-17 18:18  fwzzz  阅读(172)  评论(0编辑  收藏  举报