socket介绍
1、什么是socket
socket是应用层与传输层中间的一个软件抽象层,它是一组接口。它把TCP/IP这些复杂的协议统一封装起来
这样我们只要知道如何使用socket就好,就已经符合了传输层往下的一大串协议
2、为什么要使用socket
如果没有socket而我们写的代码又要让别人能正确解析,就需要一层层往下研究协议,写出符合协议的代码
而我们大家传输层往下的代码基本一样,所以把这些代码封装成一个模块,方便大家使用,不用去考虑传输层
以下的东西
3、socket发展
套接字起源于20世纪70年代,一开始,套接字被设计用在同一台主机上多个应用程序的通讯,这也被称为
进程间通讯或者IPC。后来网络发展,又被应用于网络协议上。
套接字有两种(或者称为有两个种族):
基于文件类型的套接字家族:名字 --> AF_UNIX
基于网络类型的套接字家族:名字 --> AF_INET被用于ipv4 AF_INET6被用于ipv6
socket用法
# 需要明确的是:
关于网络协议和socket相关概念,对于所有编程语言都是一致的,区别仅仅是个编程语言的函数名称不同
1、服务器端
import socket
# 获得socket对象 AF_INET表示基于网络的套接字
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# SOCK_STREAM表示的是TCP协议 SOCK_DGRAM表示的是UDP协议
# 将socket对象绑定ip地址和端口号
sk.bind(('127.0.0.1',8080)) # 参数需要传入一个元组
# 相当于电话的开机 括号里的参数表示可以同时接收5个请求
sk.listen(5) # 一般服务器都不需要关闭,所以加个while循环
while True:
# 进入监听状态,等待别人链接过来,有两个返回值,一个是对方的socket对象,一个是对方的ip以及端口
client,addr = sk.accept()
# 收发消息一般都需要多次循环发送,也加个while循环
while True:
try: # 如果对面强行关闭,为了程序不崩,就需要异常处理
msg = client.recv(1024) # recv表示接收,括号里是最大接收字节
if not msg: # 如果接收到的消息是空字符串,就表示对面正常退出,需要打断循环
break
# send表示发送数据,发送的数据必须是二进制数据
client.send('二进制数据'.encode('utf-8'))
except Exception as e:
print(e)
break
client.close() # 关闭对面传过来的对象
sk.close() # 关闭服务器的socket对象
2、客户端
import socket
# 获取socket对象,括号里默认是AF_INET 和 SOCK_STREAM 所以可以不写
sk = socket.socket()
# 链接到服务器端 括号里也是一个元组,包含ip地址以及对方的端口号
sk.connect(('127.0.0.1',8080)) # 自己的端口号系统会随机分配,不需要设置 while True: # 收发信息也需要循环
try: # 为了防止服务器非正常下线而导致客户端直接崩溃,需要加入异常处理
cmd = input('>>(q:退出)')
if cmd == 'q': # 给客户端一个可以正常退出的方法
break
if not cmd: # 如果直接敲回车,那么cmd就是空字符串,TCP协议对此进行优化
continue # 正常收发消息过程中,你传入空字符串,它不会帮你发送
sk.send(cmd.encode('utf-8'))
# 如果上面没有做空判断,输入空会直接跳过send这步,这样客户端和服务器端都处于接收状态,卡住不动
sk.recv(1024)
except Exception as e:
print(e)
break
sk.close() # 关闭socket对象
3、基于TCP的socket通讯流程图
socket常见问题
1、端口占用
报错信息
Traceback (most recent call last):
File "F:/pyprogram/day31/homework/topic2/test.py", line 6, in <module>
sk.bind(('127.0.0.1',8080))
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
问题发生的原因
1.可能是由于你已经启动了服务器程序,却又再次启动了服务器程序,同一个端口不能被多个进程使用导致
2.三次握手或四次挥手时,发生了异常导致对方程序已经结束而服务器任然处于time_wait状态导致
3.在高并发的场景下,由于连接的客户端太多,也会产生大量处于time_wait状态连接
解决的方案
第一种重新选择端口后就可以,或者把之前的占用端口的进程关闭掉
第二三种以后再说
2、强行关闭链接
报错信息
Traceback (most recent call last):
File "F:/pyprogram/day31/part2/客户端.py", line 12, in <module>
client.send(msg.encode('utf-8'))
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
问题发生的原因
当客户端与服务器链接成功后,如果一方没有执行close,而是直接强行终止程序(或是遇到异常被迫终止)
都会导致另一方发生问题
在windows下,接收数据的一方在recv函数处将抛出异常
解决的方案
用try...except语法抓取异常,抓到对方强退异常后并处理
3、客户端正常结束,服务器端无限循环空字符
报错信息
不会产生报错信息,但是服务器端会一直循环空字符串
问题发生的原因
客户端正常close()之后,会给服务器发送一个空字符串,
如果服务器端没有发送数据的话,就会一直循环接收这个空字符串,占用CPU资源
解决的方案
在服务器端接收的地方加上空字符串判断,如果是空字符串,就表示对方客户端退出了
那么判断空成功,让自己也退出循环