一.socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。
能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
套接字的发展:
socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字
面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件

server.py import socket # 买手机 默认得到是一个TCP的socket server = socket.socket() # 两行代码的效果是一样的 # socket的家族 AF_INET # socket的类型 # SOCK_STREAM 对应的是TCP SOCK_DGRAM 对应的是UDP # server = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) # 床架基于UDP的socket # server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) server.bind(("127.0.0.1",16888)) # 绑定手机卡 server.listen() # 开始待机 # 得到对方的socket对象与地址 client,addr = server.accept() # 接收通话请求 # 该函数会阻塞 直到有连接请求过来 print("==========") # buffersize 表示应用程序的缓冲区大小 recv其实是 从系统缓冲区读取数据到应用程序 data = client.recv(1024) # 该函数会阻塞 直到操作缓冲区有数据位置 print("==========") print("收到客户端发来的数据:%s" % data.decode("utf-8")) # 发生的数据必须为bytes类型 client.send(data) client.close() #挂断电话 server.close() # 关机 client.py import socket client = socket.socket() client.connect(("127.0.0.1",16888)) client.send("hello 服务器".encode("utf-8")) print("===================") data = client.recv(1024) # 该函数会阻塞 直到操作缓冲区有数据位置 print("===================") print("收到服务器:%s" % data.decode("utf-8")) client.close()

server.py import socket # 买手机 默认得到是一个TCP的socket server = socket.socket() # 两行代码的效果是一样的 # socket的家族 AF_INET # socket的类型 # SOCK_STREAM 对应的是TCP SOCK_DGRAM 对应的是UDP # server = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) # 创建基于UDP的socket # server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) server.bind(("127.0.0.1",16888)) # 绑定手机卡 server.listen() # 开始待机 # 连接循环 可以不断接受新连接 while True: client, addr = server.accept() # 通讯循环 可以不断的收发数据 while True: try: # 如果是windows 对方强行关闭连接 会抛出异常 # 如果是linux 不会抛出异常 会死循环收到空的数据包 data = client.recv(1024) if not data: client.close() break print("收到客户端发来的数据:%s" % data.decode("utf-8")) client.send(data) except ConnectionResetError: print("客户端强行关闭了连接") client.close() break client.close() #挂断电话 server.close() # 关机 client.py import socket client = socket.socket() client.connect(("127.0.0.1",16888)) while True: msg = input(">:") client.send(msg.encode("utf-8")) data = client.recv(1024) print("收到服务器:%s" % data.decode("utf-8")) client.close()

server.py import socket server = socket.socket() server.bind(("127.0.0.1",8888)) server.listen() client,addr = server.accept() while True: try: data = client.recv(1024) if not data: print("对方已经关闭.....") break print(data.decode("utf-8")) client.send(data.upper()) except ConnectionResetError: print("对方异常关闭连接...") client.close() server.close() client.py import socket client = socket.socket() client.connect(("127.0.0.1",8888)) client.send("hello".encode("utf-8")) data = client.recv(1024) print(data.decode("utf-8")) client.close()
半连接池:服务器的listen中的参数可以设置最大的半连接数。如果客户端的数量大于半连接数,则不会进行连接。

server.py server.bind(("127.0.0.1",8888)) # 参数可以设置最大的半连接数 最大5个 server.listen() import time while True: time.sleep(0.5) # while True: # # client,addr = server.accept() # # while True: # try: # data = client.recv(1024) # if not data: # print("对方已经关闭.....") # client.close() # break # print(data.decode("utf-8")) # client.send(data.upper()) # except ConnectionResetError: # print("对方异常关闭连接...") # client.close() # break # client.close() server.close() client.py import socket client = socket.socket() client.connect(("127.0.0.1",8888)) client.send("hello".encode("utf-8")) data = client.recv(1024) print(data.decode("utf-8")) client.close()
二.UDP协议
不可靠传输,”报头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包。不需要建立连接。
优点:传输速度快
缺点:容易丢失数据,不能保证数据的完整性
应用场景:视频聊天,语音聊天等。
# UDP协议 在创建socket是 只有一个类型不同
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,proto=0)

server.py import socket # UDP协议 在创建socket是 只有一个类型不同 server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,proto=0) server.bind(("127.0.0.1",8888)) while True: data,addr = server.recvfrom(1024) # 阻塞 直到收到数据为止 print("收到来自%s的消息:%s" % (data.decode("utf-8"),addr[0])) # 返回值为 数据 和 对方ip地址 和端口号 server.sendto(data.upper(),addr) print(res) server.close() client1.py import socket client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) while True: data = input(">>:").encode("utf-8") client.sendto(data,("127.0.0.1",8888)) d,addr = client.recvfrom(1024) print(d.decode("utf-8")) client.close() client2.py import socket client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) while True: data = input(">>:").encode("utf-8") client.sendto(data,("127.0.0.1",8888)) d,addr = client.recvfrom(1024) print(d.decode("utf-8")) client.close()