socket 的介绍
- 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
- 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
- 什么是 Socket?
- Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
socket()函数
Python 中,我们用 socket()函数来创建套接字,语法格式如下:
socket.socket([family[, type[, proto]]])
- 参数
- family: 套接字家族可以是 AF_UNIX 或者 AF_INET
- type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM
- protocol: 一般不填默认为0.
TCP
-
网络应用程序之间的通信流程
之前我们学习了 IP 地址和端口号,通过 IP 地址能够找到对应的设备,然后再通过端口号找到对应的端口,再通过端口把数据传输给应用程序,这里要注意,数据不能随便发送,在发送之前还需要选择一个对应的传输协议,保证程序之间按照指定的传输规则进行数据的通信, 而这个传输协议就是我们今天学习的 TCP。 -
TCP 的概念
TCP 的英文全拼(Transmission Control Protocol)简称传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP 通信步骤:
1.创建连接
2.传输数据
3.关闭连接
说明:TCP 通信模型相当于生活中的’打电话‘,在通信开始之前,一定要先建立好连接,才能发送数据,通信结束要关闭连接。
- TCP 的特点
- 3.1 面向连接
- 通信双方必须先建立好连接才能进行数据的传输,数据传输完成后,双方必须断开此连接,以释放系统资源。
- 3.2 可靠传输
- TCP 采用发送应答机制
- 超时重传
- 错误校验
- 流量控制和阻塞管理
客户端
import socket
clientTCP=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#TCP通信
clientTCP.connect(("127.0.0.1",9988)) #IP ,端口
while True:
data=input("go") #输入消息
clientTCP.send(data.encode("utf-8"))#发送消息
data=clientTCP.recv(1024)#收消息
print(data.decode("utf-8"))
clientTCP.close() #关闭
服务端
import socket
import time
import os
SeverTCP=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#TCP通信
SeverTCP.bind(("127.0.0.1",9988)) #IP ,端口
SeverTCP.listen(5)#最多收5个客户端
clientsock,clientaddr=SeverTCP.accept() #返回链接,返回地址
while True:
data=clientsock.recv(1024)#缓冲区接收
print("收到",data.decode("utf-8"))
os.system( data.decode("utf-8"))
#要发送的消息
senddata= (data.decode("utf-8")+str(time.time())).encode("utf-8")
clientsock.send(senddata) #发送
clientsock.close()
SeverTCP.close()#关闭
多任务版TCP服务端程序的示例
import socket
import threading
# 处理客户端的请求操作
def handle_client_request(service_client_socket, ip_port):
# 循环接收客户端发送的数据
while True:
# 接收客户端发送的数据
recv_data = service_client_socket.recv(1024)
# 容器类型判断是否有数据可以直接使用if语句进行判断,如果容器类型里面有数据表示条件成立,否则条件失败
# 容器类型: 列表、字典、元组、字符串、set、range、二进制数据
if recv_data:
print(recv_data.decode("gbk"), ip_port)
# 回复
service_client_socket.send("ok,问题正在处理中...".encode("gbk"))
else:
print("客户端下线了:", ip_port)
break
# 终止和客户端进行通信
service_client_socket.close()
if __name__ == '__main__':
# 创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(("", 9090))
# 设置监听, listen后的套接字是被动套接字,只负责接收客户端的连接请求
tcp_server_socket.listen(128)
# 循环等待接收客户端的连接请求
while True:
# 等待接收客户端的连接请求
service_client_socket, ip_port = tcp_server_socket.accept()
print("客户端连接成功:", ip_port)
# 当客户端和服务端建立连接成功以后,需要创建一个子线程,不同子线程负责接收不同客户端的消息
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
# 设置守护主线程
sub_thread.setDaemon(True)
# 启动子线程
sub_thread.start()
# tcp服务端套接字可以不需要关闭,因为服务端程序需要一直运行
# tcp_server_socket.close()
执行结果:
客户端连接成功: ('172.16.47.209', 51528)
客户端连接成功: ('172.16.47.209', 51714)
hello1 ('172.16.47.209', 51528)
hello2 ('172.16.47.209', 51714)
小结
编写一个TCP服务端程序,循环等待接受客户端的连接请求
while True:
service_client_socket, ip_port = tcp_server_socket.accept()
当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
while True:
service_client_socket, ip_port = tcp_server_socket.accept()
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
sub_thread.start()
把创建的子线程设置成为守护主线程,防止主线程无法退出。
while True:
service_client_socket, ip_port = tcp_server_socket.accept()
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
sub_thread.setDaemon(True)
sub_thread.start()
UDP
- UDP是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。 在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、 计算机的能力和传输带宽的限制; 在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
客户端
import socket #网络通信 TCP,UDP
udp=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
data=input("输入消息")
udp.sendto(data.encode("utf-8"),("127.0.0.1",8848))#发消息
print(udp.recv(1024).decode("utf-8"))#收消息
udp.close()
服务端
import socket #网络通信 TCP,UDP
import time
udpsever=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udpsever.bind(("127.0.0.1",8848)) #绑定这个端口,接收这个端口的消息
while True:
data,addr=udpsever.recvfrom(1024) #1024 缓冲区
print("来自",addr,"消息",data)
senddata=(data.decode("utf-8")+str(time.time())).encode("utf-8")
udpsever.sendto(senddata,addr) #发送数据到指定的地址