在 Python 开发中,socket 库是一个非常重要的工具,它允许我们进行网络编程。通过 socket,程序可以在不同的机器之间进行通信,这为我们构建分布式系统、网络服务等提供了基础。无论是开发一个简单的聊天程序,还是构建复杂的网络服务架构,socket 都是不可或缺的。本文将结合详细的 Python 代码,全面介绍 socket 库的使用方法,让你轻松掌握网络编程的核心技能。
什么是 socket?
socket(套接字)是通信的端点,它是一个通信会话的抽象。在计算机网络中,两台计算机之间要进行通信,就需要在双方建立 socket 连接,通过这个双向通道进行数据的发送和接收。
socket 的类型
主要有两种常见的 socket 类型:
SOCK_STREAM :提供面向连接的、可靠的字节流服务,基于 TCP 协议。它确保数据按照顺序到达,并且在数据传输过程中不会出现重复。
SOCK_DGRAM :提供无连接的、不可靠的 datagram 服务,基于 UDP 协议。这种类型的数据传输不保证可靠性,数据可能会丢失或乱序到达,但它的传输速度相对较快,适用于对实时性要求较高但对数据丢失不太敏感的场景,如视频流传输。
在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。python中socket模块为操作系统的socket实现提供了一个python接口。
在Python中,import socket后,用socket.socket()方法来创建套接字,语法格式如下:
sk = socket.socket([family[, type[, proto]]])
参数说明:
♥family: 套接字家族,可以使AF_UNIX或者AF_INET。
♥type: 套接字类型,根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM,也就是TCP和UDP的区别。
♥protocol: 一般不填默认为0。
直接socket.socket(),则全部使用默认值。
socket 的作用和可以实现的功能主要包括:
网络通信:socket 可以让计算机之间通过网络发送和接收数据,就像人们通过电话交谈一样。
文件传输:使用 socket,可以在不同的计算机之间传输文件,就像发送电子邮件附件一样。
远程控制:socket 可以实现远程控制功能,比如远程桌面,你可以在家里的电脑上控制办公室的电脑。
在线游戏:网络游戏中的数据交换就是通过 socket 实现的,它允许玩家之间实时互动。
即时通讯:像QQ、微信这样的即时通讯软件,它们的消息传输也是通过 socket 实现的。
网站浏览:当你在浏览器中输入一个网址时,浏览器通过 socket 与网站服务器通信,获取网页内容并显示给你。
网络服务:各种网络服务,如电子邮件、天气预报等,都是通过 socket 提供服务的。
关于s=socket.socket(family,type)还有一些实例方法
(1)s.bind((address,port))
将socket绑定到一个地址和端口上,通常用于socket服务端;
address必须是一个双元素元组,((host,port)),主机名或者IP地址+端口号。如果端口号正在被使用或者主机名或IP地址错误,则引发socket.error异常。端口号的使用是有限制的,在linux或者unix之中只有系统管理员才能使用1024以下的端口,这些端口号用于标准服务。
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(("172.16.0.18",30000))
(2)s.getsockname()
s.getsockname()返回一个客户机socket,带有客户机端的地址信息。accept方法返回一个双元素元组,形如(IP,port)。第一个元素是客户的IP地址,第二个元素是端口号。
(3)s.listen(backlog)
将socket设置成监听模式,可以监听backlog外来的连接请求,让服务器开始监听客户端发来的连接请求
这个方法设置服务器为监听状态,backlog制定了最多的连接数,至少为1。接到连接请求后,这些请求必须排队,如果队列达到backlog的数值,则拒绝接下来的连接请求。
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(("172.16.0.18",555))
s.listen(10)
(4)s.connect((address,port)) 与s.connect_ex((address,port))
将socket连接到定义的主机和端口上,通常用于socket客户端
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
state=sock.connect(("172.16.0.18",136))
二者的区别是:connect_ex 函数在遇到C层面的异常时不会抛出异常,而是返回状态码,0状态码表示正常,你也可以使用connect方法进行连接,但这样,就需要使用异常捕获机制来捕获ConnectionRefusedError 异常。
(5)s.recv(buflen[,flags])
从socket中接收数据,最多接收buflen个字符,一般填写1024个
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("172.16.0.18",555))
data=s.recv(1024)
(6)s.send(data[,flags])
通过socket发送指定的数据
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(("172.16.0.18",555))
s.listen(10)
connection,address=s.accept()
print( connectionsocket._socketobject object at 0x01DDDCE0>
>>> print address
('172.16.0.18', 21586)
s.send('hello,I am lybbn.cn')
Traceback (most recent call last):
File "
socket.error: [Errno 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数
据报套接字时)
由上可见,s.send()发送数据的时候需要先建立socket连接,不然会出现error。使用recv方法和send方法发送和接受消息。发送和接收都是采用的字符串的形式。send方法返回已发送的字符个数。调用recv的时候,必须制定一个整数来控制本次调用所接受的最大的数据量。
recv方法接收数据时会进入阻塞状态,最后返回一个字符串,表示收到的数据。如果发送数据超过recv所允许,数据会被截断。多余的数据将缓冲于接收端。以后调用recv时,多余的数据会从缓冲区删除。
(7)s.close()
关闭socket连接,传输结束,通过调用close方法关闭连接。
TCP 服务器端server.py代码示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port = ("172.16.0.18", 30000)
try:
sk = socket.socket() # 创建套接字
sk.bind(ip_port) # 绑定服务地址、绑定端口
sk.listen(5) # 监听连接请求
print('启动socket服务端服务,等待客户端连接...')
conn, address = sk.accept() # 等待连接,此处自动阻塞
while True: # 一个死循环,直到客户端发送‘exit’的信号,才关闭连接
client_data = conn.recv(1024).decode() # 接收信息
if client_data == "exit": # 判断是否退出连接
exit("通信结束")
print("来自%s的客户端向你发来信息:%s" % (address, client_data))
conn.sendall('服务器已经收到你的信息'.encode()) # 回馈信息给客户端
conn.close() # 关闭连接
except Exception as msg:
print('socket,服务端发送失败{}!'.format(msg))
TCP客户端client.py:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port = ("172.16.0.18", 30000)
s = socket.socket() # 创建套接字
s.connect(ip_port) # 连接服务器
while True: # 通过一个死循环不断接收用户输入,并发送给服务器
inp = input("请输入要发送的信息:").strip()
if not inp: # 防止输入空信息,导致异常退出
continue
s.sendall(inp.encode())
if inp == "exit": # 如果输入的是‘exit’,表示断开连接
print("结束通信!")
break
server_reply = s.recv(1024).decode()
print(server_reply)
s.close() # 关闭连接
代码分析
在服务器端,bind() 方法用于绑定 IP 地址和端口,使得服务器能够监听指定地址和端口上的连接请求。listen() 方法则让服务器进入监听状态,等待客户端的连接。
accept() 方法是一个阻塞方法,它会等待客户端的连接。当客户端连接成功后,会返回一个新的 socket 对象(用于和客户端进行通信)和客户端的地址信息。
客户端通过 connect() 方法连接到服务器的指定 IP 地址和端口。
数据的发送和接收分别通过 send() 和 recv() 方法实现,需要注意数据是以字节流的形式传输,所以在发送之前要使用 encode() 方法将字符串编码为字节,接收后使用 decode() 方法解码为字符串。
目前实盘中,需要向外网传输不同数据类型的数据,会将不能类型维护成不同的字段,进行传输,
if sockname=='mail':
df_dict = send_data.to_dict('records')
json_data = "bb#" + json.dumps(df_dict) + "bb#"
print('send_maild', json_data)
tcp_client.send(json_data.encode('utf-8'))
# 4. 等待服务端数据返回 最大接收1024个字节
recv_data = tcp_client.recv(1024)
print(recv_data.decode('utf-8'))
服务端将会对不同类型,在进行拆分,邮件发送给不同的人
当时,本文主要是掌握socket的传输,如何传输、传输什么内容、实时监控是否传输、异常响应等都需要再此基础上进行挖掘,暂不过多介绍,有更新在进行拆分详解。