基于TCP协议的socket套接字编程
一、什么是Scoket
Scoket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tc/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
- 注意:也有人将scoket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip和port的绑定就标识了互联网独一无二的一个应用程序,而程序的pid是同一台机器上的不同进程或者线程的标识。
二、基于TCP协议的套接字编程(简单)
客户端
import socket
client = socket.socket() # 拿电话
# 拨电话,指定服务器ip号和端口号
client.connect(('127.0.0.1',8080))
client.send(b'hello world!') # 通信发送信息
data = client.recv(1024) # 接收信息
print(data)
client.close() # 挂点电话
服务端
import socket
server = socket.socket() # 买手机,不传参数默认用的是TCP协议
# 插入手机卡
server.bind(('127.0.0.1',8080)) # 127.0.0.1本机回还地址 特点:只能识别自己
server.listen(5) # 开机,半连接池,限制的是请求数
print('start....')
# 接听电话
conn,addr = server.accept() # (三次握手建立的双向连接,(客户端的ip和端口号))
data = conn.recv(1024) # 最大接收的字节数
print(data)
conn.send(b'hello baby~') # 发送信息
# 挂掉电话
conn.close()
# 关机
server.close()
三、基于TCP协议的套接字编程(循环)
服务端:
import socket
"""
服务端
固定的ip地址和port
24小时不间断提供服务
"""
server = socket.socket()
server.bind(('127.0.0.1',8080)) # 绑定ip和port
server.listen(5) # 半连接池
while True:
conn,addr = server.accept() # 等别人来,conn就类似于双向通道
print(addr) # ('127.0.0.1',5132)客户端的地址
while True:
# 客户端断开连接后,服务器会报错使用异常处理。
try:
data = conn.recv(1024)
print(data) # b"" 针对mac与linux客户端异常退出之后,服务端不会报错,只会一直收b""
if len(data) == 0:break
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break # 不中断的话会一直打印错误信息
conn.close()
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
msg = input('>>>:').encode('utf-8')
if len(msg) == 0:continue
# if not msg:continue
client.send(msg) # 发送的信息可以自己输入
data = client.recv(1024)
print(data)
在服务器连接客户端1时,虽然也能监听到客户端2,但不能给客户端2返回消息,将客户端1关闭后即断开连接。给客户端2返回数据。
四、地址占用问题
有的同学在重启服务端时,可能会遇到:
这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址。(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)
# 加入一条socket配置,重用ip和端口
server = socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
server.bind(('127.0.0.1',8080))