python核心编程笔记——网络编程(二)

本文深入探讨Python网络编程,详细讲解socket模块的使用,包括TCP和UDP服务器与客户端的创建,以及套接字对象的各种方法和属性。适合初学者和进阶者掌握网络编程核心技能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ps:我的python环境为3.6.2

Python中的网络编程

. 本节中使用的主要模块是socket模块,在模块中可以找到socket()函数,该函数用于创建套接字对象。套接字也有自己的方法集,这些方法可以实现基于套接字的网络通信。

socket()模块函数

. 创建套接字要使用socket.socket()函数,其一般语法如下:

socket(socket_family,socket_type,protocol = 0)

. 其中socket_family是AF_UNIX或AF_INET,socket_type是SOCK_STREAM或SOCK_DGRAM,protocol通常省略,默认为0。因此创建TCP或UDP套接字的形式如下:

tcpSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
udpSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

. 在有了套接字对象后,就可以使用套接字对象的方法来做进一步交互。对Python来说,可以使用“from socket import * ”将socket属性引入命名空间中,这样能够大大缩短代码:

tcpSock = socket.socket(AF_INET,SOCK_STREAM)

套接字对象(内置)方法

. 以下的表列举了一些常见的套接字对象的方法和属性:

名称描述
服务器套接字方法
s.bind()将地址(主机号、端口号对)绑定到套接字上
s.listen()设置并启动TCP监听器
s.accept()被动接受TCP客户端连接,一直等待知道连接到达(阻塞),每个连接进来的客户端,都会通过accept函数返回一个不同的客户端的socket对象和属于客户端的套接字
客户端套接字方法
s.connect()主动发起TCP服务器连接
s.connect_ex()上个方法的升级版,会以错误码的形式返回问题,而不是抛出异常
普通的套接字方法
s.recv()接受TCP消息
s.recv_into()接受TCP消息到指定的缓冲区
s.send()发送TCP消息
s.sendall()完整的发送TCP消息
s.recvfrom()接受UDP消息
s.recvfrom_into()接受UDP消息到指定的缓冲区
s.sendto()发送UDP消息
s.getpeername()连接到套接字(TCP)的远程地址
s.getsockname()当前套接字的地址
s.getsockopt()返回给定套接字选项的值
s.setsockopt()设置给定套接字选项的值
s.shutdown()关闭连接
s.close()关闭套接字
s.detach()在未关闭文件描述符的情况下关闭套接字,返回文件描述符
s.ioctl()控制套接字的模式(仅支持 Windows)
面向阻塞的套接字方法
s.setblocking()设置套接字的阻塞或非阻塞模式
s.settimeout()设置阻塞套接字操作的超时时间
s.gettimeout()获取阻塞套接字操作的超时时间
面向文件的套接字方法
s.fileno()套接字的文件描述符
s.makefile()创建与套接字关联的文件对象
数据属性
s.family套接字家族
s.type套接字类型
s.proto套接字协议

创建TCP服务器

. 首先看看创建TCP服务器的一般伪代码:

ss = socket()					#创建服务器套接字
ss.bind()						#套接字与地址绑定
ss.listen()						#监听连接
inf_loop:						#服务器无限连接
	cs = ss.accept()			#接受客户端连接
	comm_loop:					#通信循环
		cs.recv()/cs.send()		#对话(接受/发送)
	cs.close()					#关闭客户端套接字
ss.close()						#关闭服务器套接字(可选)

. 所有的套接字都通过socket()函数来创建对象,因为服务器需要占用一个端口等待客户端的请求,所以需要绑定一个本地地址,特别的TCP服务器必须监听(传入的)连接,一旦这个过程完了之后,服务器就可以开始它的无限循环。
  调用accept()函数之后,就开启了一个简单的(单线程)服务器,它会等待客户端的连接。默认情况下,accept()是阻塞的,这意味着执行将被暂停,直到一个连接到达(套接字也确实支持非阻塞模式)。
  一旦服务器接收了一个连接,就会返回(利用accept())一个独立的客户端套接字,用来与即将到来的消息进行交换。也就是说,当一个传入的请求到达时,服务器会创建一个新的通信端口来直接与客户端进行通信,再次空出主要的端口(原始服务器套接字),以使其能够接受新的客户端连接;而通过使用这个新的套接字,客户端和服务器之间就可以开始参与发送和接受的对话中,直到连接终止(当一方关闭连接或者向对方发送一个空字符串时,通常就会关闭连接)。
  接下来创建一个TCP服务器,它接受来自客户端的消息,并返回加了时间戳的相同内容的消息:

from socket import *
from time import ctime

Host = ''
Port = 21567
Bufsize = 1024
Addr = (Host,Port)

tcpSerSock = socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(Addr)
tcpSerSock.listen(5)

while True:
    print('waiting for connection...')
    tcpCliSock,addr = tcpSerSock.accept()
    print('...connected from:',addr)

    while True:
        data = tcpCliSock.recv(Bufsize)
        if not data:
            break
        senddata = '[%s] %s.' % (bytes(ctime(),'utf-8'),data)
        tcpCliSock.send(senddata.encode())

    tcpCliSock.close()
tcpSerSock.close()


创建TCP客户端

. 创建客户端相对要简单一些,首先还是看看伪码:

cs = socket() 				#  创建客户端套接字
cs.connect() 				#  尝试连接服务器
comm_loop:					#  通信循环
	cs.send()/cs.recv() 	#  对话(发送 / 接收)
cs.close() 					#  关闭客户端套接字

. 一旦客户端拥有了一个套接字,它就可以利用套接字的connect()方法直接创建一个到服务器的连接,当连接建立之后就可以参与到与服务器的对话中,最后,如果客户端完成了它的事务,它就可以关闭套接字,终止此次连接。
  以下是TCP客户端的代码示例:

from socket import *

Host = 'localhost'
Port = 21567
Bufsize = 1024
Addr = (Host,Port)

tcpCliSock = socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(Addr)

while True:
    data = input('> ')
    if not data:
        break
    
    tcpCliSock.send(data.encode())
    data = tcpCliSock.recv(Bufsize)
    if not data:
        break
    print(data.decode('utf-8'))

tcpCliSock.close()

. 客户端也有一个无限循环,但是不会意味着它会永远运行下去,客户端循环在以下两种情况将会跳出:用户没有输入;或者服务器终止且对recv()方法的调用失败。

执行TCP服务器和客户端

. 首先要运行的应该是服务器,因为如果先运行客户端,那么将无法进行任何连接。服务器可以视为一个被动伙伴,因为必须首先建立自己,然后被动地等待连接。另一方面,客户端是一个主动的合作伙伴,因为它主动发起一个连接。

创建UDP服务器

. UDP服务器不需要TCP服务器那么多设置,因为它不是面向连接的,除了等待传入的连接外,几乎不需要做任何其他工作。它的伪码如下:

ss = socket()									#  创建服务器套接字
ss.bind() 										#  绑定服务器套接字
inf_loop:										#  服务器无限循环
	cs = ss.recvfrom()/ss.sendto()  			#  关闭(接收 / 发送)
ss.close()  									#  关闭服务器套接字(可选)

. 从上面的伪代码中可以看出,除了普通的创建套接字并将其绑定到本地地址(主机名/端口号对)外,并没有额外的工作。UDP 和 TCP 服务器之间的另一个显著差异是,因为数据报套接字是无连接的,所以就没有为了成功通信而使客户端连接到一个独立的套接字“转换“的操作,以下是UDP加时间戳的服务器代码示例:

from socket import *
from time import ctime

Host = ''
Port = 21567
Bufsize = 1024
Addr = (Host,Port)

udpSerSock = socket(AF_INET,SOCK_DGRAM)
udpSerSock.bind(Addr)

while True:
    print('waiting for message...')
    data,addr = udpSerSock.recvfrom(Bufsize)
    senddata = '[%s] %s.' % (bytes(ctime(),'utf-8'),data)
    udpSerSock.sendto(senddata.encode(),addr)
    print('...received from and returned to :',addr)

udpSerSock.close()

创建UDP客户端

. UDP的客户端代码是最短的,它的伪代码如下:

cs = socket() 							#  创建客户端套接字
comm_loop: 								#  通信循环
	cs.sendto()/cs.recvfrom()  			#  对话(发送 / 接收)
cs.close() 								#  关闭客户端套接字

. 一旦创建了套接字对象,就进入对话循环中与服务器进行信息交互,最后当通信结束的时候会关闭套接字。以下是UDP的时间戳客户端代码示例:

from socket import *

Host = 'localhost'
Port = 21567
Bufsize = 1024
Addr = (Host,Port)

udpCliSock = socket(AF_INET,SOCK_DGRAM)

while True:
    data = input('> ')
    if not data:
        break
    udpCliSock.sendto(data.encode(),Addr)
    data,Addr = udpCliSock.recvfrom(Bufsize)
    if not data:
        break
    print(data)

udpCliSock.close()

socket模块属性

. 除了现在熟悉的 socket.socket()函数之外,socket 模块还提供了更多用于网络应用开发的属性,下标列出了常用的属性:

属性名描述
数据属性
AF_UNIX、AF_INET、AF_INET6 、AF_NETLINK 、AF_TIPCPython 中支持的套接字地址家族
SO_STREAM、SO_DGRAM套接字类型(TCP=流,UDP=数据报)
has_ipv6指示是否支持 IPv6 的布尔标记
异常
error套接字相关错误
herror主机和地址相关错误
gaierror地址相关错误
timeout超时时间
函数
socket()以给定的地址家族、套接字类型和协议类型(可选)创建一个套接字对象
socketpair()以给定的地址家族、套接字类型和协议类型(可选)创建一对套接字对象,函数用于创建一对无名的、相互连接的套接字。 如果函数成功,则返回0,创建好的套接字分别是sv[0]和sv[1];否则返回-1,错误码保存于errno中
create_connection()常规函数,它接收一个地址(主机名,端口号)对,返回套接字对象
fromfd()以一个打开的文件描述符创建一个套接字对象
ssl()通过套接字启动一个安全套接字层连接;不执行证书验证
getaddrinfo()获取一个五元组序列形式的地址信息
getnameinfo()给定一个套接字地址,返回(主机名,端口号)二元组
getfqdn()返回完整的域名
gethostname()返回当前主机名
gethostbyname()将一个主机名映射到它的 IP 地址
gethostbyname_ex()gethostbyname()的扩展版本,它返回主机名、别名主机集合和 IP 地址列表
gethostbyaddr()将一个 IP 地址映射到 DNS 信息;返回与 gethostbyname_ex()相同的 3 元组
getprotobyname()将一个协议名(如‘tcp’)映射到一个数字
getservbyname()/getservbyport()将一个服务名映射到一个端口号,或者反过来;对于任何一个函数来说,协议名都是可选的
ntohl()/ntohs()将来自网络的整数转换为主机字节顺序
htonl()/htons()将来自主机的整数转换为网络字节顺序
inet_aton()/inet_ntoa()将 IP 地址八进制字符串转换成 32 位的包格式,或者反过来(仅用于 IPv4 地址)
inet_pton()/inet_ntop()将IP 地址字符串转换成打包的二进制格式,或者反过来(同时适用于 IPv4 和IPv6 地址)
getdefaulttimeout()/setdefaulttimeout()以秒(浮点数)为单位返回默认套接字超时时间;以秒(浮点数)为单位设置默认套接字超时时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值