基于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))