Socket是TCP/IP提供的外部编程接口,是对TCP/IP的封装和应用。
Socket被称作“套接字”,用于描述IP地址和端口,是一个通信、链的句柄,可以实现不同虚拟机 或不同计算机之间的通信。网络上的两个程序通过一个双向的通信连接实现数据的交换,应用程序通过“套接字”向网络发出请求或者应答网络请求。
Socket模块的主要目的是帮助在网络上的两个程序之间建立信息通道。在Python中提供了两个基本的Socket模块:服务端Socket和客户端Socket。
当创建一个服务端Socket之后,这个Socket就会在本机的一个端口上等待连接,客户端Socket会访问这个端口,当两者完成连接之后,就可以进行交互了。
使用Socket编写一个简单的服务端和客户端
多线程服务端程序:
# -*- coding: UTF-8 -*-
import socket # 导入 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:
message = recv_data.decode()
print(message)
print(ip_port)
# 回复
service_client_socket.send("已接收...".encode())
else:
print("客户端下线了:", ip_port)
print()
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()
客户端程序:
# -*- coding: UTF-8 -*-
import socket # 导入 socket 模块
if __name__ == '__main__':
sk = socket.socket() # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 9090 # 设置端口号
sk.connect((host, port))
message = "connect······"
sk.sendall(message.encode())
print(sk.recv(1024).decode())
i = 0
while True:
try:
message = input(">>>")
sk.send(message.encode())
print(sk.recv(1024).decode())
except KeyboardInterrupt:
break
sk.close()
运行结果:
Socket实例化
格式:
sk = socket.socket(family,type[,protocal])
family:地址族,常用地址族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX、UNIX域Socket)、AF_ROUTE等。默认为AF_INET,通常使用默认即可。
type:Socket类型
SOCK_STREAM,TCP类型(默认)
SOCK_DGRAM,UDP类型
SOCK_RAM,原始类型,允许对底层协议如IP或ICMP进行直接访问,基本用不到。
protocol:可选项,使用的协议,通常赋值“0”,由系统自动选择。
Socket常用函数
- bind()
由 服务端 Socket调用,将之前创建Socket与指定的IP地址和端口进行绑定。
sk.bind(("127.0.0.1",9090))
- listen()
用于在使用TCP的服务端开启监听模式。
#服务端开启一个监听,最大连接数为5
sk.listen(128)
- accept()
用于在使用TCP的服务端接收连接,一般是阻塞态。接受TCP连接并返回(conn, adress),其中conn为新的套接字对象,可以用来接收和发送数据,address为连接客户端的地址。
socket_new, port = sk.accept()
- connect()
用于在使用TCP的客户端去连接服务端时使用
sk.connect(("127.0.0.1", 9090))
- send()
用于在使用TCP时发送数据,可能未将指定的内容全部发送。
sk.send(string[,flag]) #返回值是发送字节数量
- sendall()
用于在使用TCP时发送数据,完整发送TCP数据。
# 发送一段字符到Socket
sk.sendall("Hello!".encode())
# 成功返回None,失败抛出异常
- recv()
用于使用TCP接收数据。
sk.recv(bufsize[,flag])
# bufsize指定最多可以接收的数量
-
sendto()
用于使用UDP时发送数据 -
recvfrom()
UDP专用,接收数据,返回远端的IP地址和端口 -
close()
关闭Socket