目录
一、客户端/服务器架构
- C/S=Client/Server
- B/S=Brower/Server
- 一个基于客户端/服务器,一个基于浏览器/服务器
- 互联网中处处是C/S架构,如电影网站是服务端你的浏览器是客户端(B/S架构也是 C/S架构的一种)
C/S架构与socket的关系:我们学习socket就是为了完成C/S架构的开发
二、osi五层|七层
一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件台计算机系统就可以自己跟自己玩了(打个单机游戏,玩个扫雷哈的)。
如果你要跟别人一起玩,那你就需要上网了,什么是互联网?
互联网的核心就是由一堆协议组成,协议就是标准,比如全世界人通信的标准是英语,如果把计算机比作人,互联网协议就是计算机界的英语。所有的计算机都采用了互联网协议,那所有的计算机都就可以按照统一的标准去收发信息从而完成通信了。
人们按照分工不同把互联网协议从逻辑上划分了层级:
每层都运行特定的协议,越往上越靠近用户,越往下越靠近硬件,用户感知到的只是最上面一层应用层,自上而下每层都依赖于下一层。
1、物理层由来:上面提到,孤立的计算机之间要想一起玩,就必须接入 internet,言外之意就是计算机之间必须完成组网
物理层功能:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字02、数据链路层由来:单纯的电信号0和1没有任何意义,必须规定电信号多少位一组每组什么意思
数据链路层的功能:定义了电信号的分组方式以太网协议:早期的时候各个公司都有自己的分组方式,后来形成了统一的标准即以太网协议ethernet
mac地址:每块网卡出厂时都被烧制上一个世界唯一的mac地址长度为48位2进制通常由12位16进制数表示(前六位是厂商编号后六位是流水线号)广播:有了mac地址同一网络内的两台主机就可以通信了(一台主机通过arp协议获取另外一台主机的mac地址)ethernet 采用最原始的方式,广播的方式进行通信即计算机通信基本靠吼
3、网络层由来:有了ethernet、mac地址、广播的发送方式世界上的计算机就可以彼此通信了,问题是世界范围的互联网是由一个个彼此隔离的小的局域网组成的,那么如果所有的通信都采用以太网的广播方式,那么一台机器发送的包全世界都会收到,这就不仅仅是效率低的问题了,这会是一种灾难。
网络层功能:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址。
4、传输层的由来:网络层的ip帮我们区分子网,以太网层的mac帮我们找到主机,然后大家使用的都是应用程序,你的电脑上可能同时开启qq,暴风影音,等多个应用程序那么我们通过ip和mac找到了一台特定的主机,如何标识这台主机上的应用程序,答案就是端口,端口即应用程序与网卡关联的编号。
传输层功能:建立端口到端口的通信。
5、应用层由来:用户使用的都是应用程序,均工作于应用层,互联网是开发的,大家都可必须规定好数据的组织形式以开发自己的应用程序,数据多种多样。
应用层功能:规定应用程序的数据格式
三、socket是什么?
我们经常把socket 翻译为套接字,Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket 其实是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket 接口后面,对用户来说,一组简单的接口就是全部让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解 tcp/udp 协议,socket 已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这合机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序。而程序的pid是同一台机器上不同进程或者线程的标识。
四、套接字
套接字起源于20世纪70年代加利福尼亚大学伯克利分校版本的Unix。一开始套接字被设计用在同一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或IPC。套接字有两种(或者称为有两个种族,分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族套接宇家族的名字: AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字: AF INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中AF_INET是使用最广泛的一个,python 支持很多种地址家族但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
1、套接字工作流程
工作流程:
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用 accept()阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端客户端读取数据,最后关闭连接,一次交互结束。
tcp的整个流程类似打电话的一个过程:
服务端:
a. 买于机tcp_server=socket.socket(socket.AF_INET,socket.SOCKSTREAM)b. 绑定电话卡tcp_server.bind(("ip”,端口))
c. 待机tcp_server.listen(最大链接数)
d. 接听电话conn,addr=tcp_serveraccept()得到链接和对方地址e. 接收消息,听话data=connrecv(接收字节数)
g. 挂电话conn.close()
h. 手机关机tcp_server.close()
客户端:
a. 买手机tcp_client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
b. 拨号tcp_client.connect(("ip",服务端端口))
c. 发消息,说话tcp_client.send(发送的是字节数据需要编码)
d. 接收消息,听话data=tcpclient.recv(1024)
e. 挂电话tcpclient.close()
2、基于tcp协议的套接字编程
- tcp 是基于连接的,必须先启动服务端,然后再启动客户端去连接服务端
- tcp 的 recv()和 send()没有对应关系,都是从各自的缓冲区进行操作
① 实现一次通信
tcp_server.py:
import socket
# 建立socket连接(AF_INET:基于网络套接字家族,SOCK_STREAM:tcp协议)
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买手机
# 声明服务端的ip和端口
tcp_server.bind(('127.0.0.1', 8000)) # 绑定电话卡
# 声明最大连接数
tcp_server.listen(5) # 待机
# 得到一个持续连接和客户端地址
conn, addr = tcp_server.accept() # 接听电话
# 接收客户端的消息
data = conn.recv(1024) # 听消息,听话
print('客户端说:', data.decode('utf-8'))
# 发送信息给客户端
conn.send('hello我是服务端...'.encode('utf-8')) # 发消息,说话
# 断开与客户端的连接
conn.close() # 挂电话
# 关闭服务器
tcp_server.close() # 关机
tcp_client.py:
import socket
# 建立socket连接(AF_INET:基于网络套接字家族,SOCK_STREAM:tcp协议)
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买手机
# 与服务端建立连接
tcp_client.connect(('127.0.0.1', 8000)) # 拨号
# 发送消息给服务器
tcp_client.send('hello我是客户端'.encode('utf-8')) # 说话
# 接收服务端的信息
data = tcp_client.recv(1024) # 听话
print("服务端说:", data.decode('utf-8'))
# 断开与服务器的连接
tcp_client.close() # 挂电话
② 实现通信循环
tcp_server_forever.py:
import socket
# 建立socket连接(AF_INET:基于网络套接字家族,SOCK_STREAM:tcp协议)
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买手机
# 声明服务端的ip和端口
tcp_server.bind(('127.0.0.1', 8000)) # 绑定电话卡
# 声明最大连接数
tcp_server.listen(5) # 待机
# 得到一个持续连接和客户端地址
conn, addr = tcp_server.accept() # 接听电话
while True:
# 接收客户端的消息
data = conn.recv(1024) # 听消息,听话
if not data.strip():
break
# 如果客户端发送exit,中断连接
if 'exit' == data.decode('utf-8'):
conn.send(data)
break
print('客户端说:', data.decode('utf-8'))
# 发送信息给客户端
msg = input(">>:")
conn.send(msg.encode('utf-8')) # 发消息,说话
# 断开与客户端的连接
conn.close() # 挂电话
# 关闭服务器
tcp_server.close() # 关机
tcp_client_forever.py:
import socket
# 建立socket连接(AF_INET:基于网络套接字家族,SOCK_STREAM:tcp协议)
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买手机
# 与服务端建立连接
tcp_client.connect(('127.0.0.1', 8000)) # 拨号
while True:
msg = input(">>:")
if not msg.strip():
continue
# 发送消息给服务器
tcp_client.send(msg.encode('utf-8')) # 说话
# 接收服务端的信息
data = tcp_client.recv(1024) # 听话
if 'exit' == data.decode('utf-8'):
print('与服务器断开连接,如需发送请重连')
break
print("服务端说:", data.decode('utf-8'))
# 断开与服务器的连接
tcp_client.close() # 挂电话