《Python网络编程攻略》学习笔记
TCP用主机的IP地址加上主机的端口号作为TCP连接的端点,这种端点叫做套接字(socket)或插口
获取远程设备的IP地址
import socket
def get_remote_machine_info():
remote_host = "www.python.org"
try:
print("IP address: %s" % socket.gethostbyname(remote_host)) #参数为远程设备的主机名
except socket as err_msg:
print("%s: %s" %(remote_host, err_msg))
if __name__ == "__main__":
get_remote_machine_info()
# IP address: 151.101.228.223
将IPv4地址转换成不同的格式
import socket
from binascii import hexlify
def convert_ip4_address():
for ip_addr in ["127.0.0.1", "192.168.0.1"]:
packed_ip_addr = socket.inet_aton(ip_addr)
unpacked_ip_addr = socket.inet_ntoa(packed_ip_addr)
print("IP address: %s => packed : %s, Unpacked : %s" % (ip_addr, hexlify(packed_ip_addr).decode(), unpacked_ip_addr))
if __name__ == "__main__":
convert_ip4_address()
# IP address: 127.0.0.1 => packed : 7f000001, Unpacked : 127.0.0.1
# IP address: 192.168.0.1 => packed : c0a80001, Unpacked : 192.168.0.1
通过指定的端口和协议找到服务名
import socket
def find_servert_name():
protocolname = "tcp"
for port in [80, 25]:
# socket.getservbyport(port, protocolname)传入端口号和协议名返回服务名
print("Port: %s => service name: %s" % (port, socket.getservbyport(port, protocolname)))
print("Port: %s => service name: %s" % (53, socket.getservbyport(53, "udp")))
if __name__ == "__main__":
find_servert_name()
# Port: 80 => service name: http
# Port: 25 => service name: smtp
# Port: 53 => service name: domain
主机字节序和网络字节序之间相互转换
import socket
def convert_integer():
data = 1234
# 32-bit
# socket.ntohl(data)将32位整数从网络字节序转换为长整型主机字节顺序.
# socket.htohl(data)将32位整数从长整型主机字节序转换为网络字节顺序。
print("Original: %s => Long host byte order: %s, Network byte order: %s" % (data, socket.ntohl(data), socket.htonl(data)))
#16-bit
# socket.ntohs(data)将16位整数从网络字节序转换为短整型主机字节顺序.
# socket.htohs(data)将16位整数从短整型主机字节序转换为网络字节顺序.
print("Original: %s => Short host byte order: %s, Network byte order: %s" % (data, socket.ntohs(data), socket.htons(data)))
if __name__ == "__main__":
convert_integer()
# Original: 1234 => Long host byte order: 3523477504, Network byte order: 3523477504
# Original: 1234 => Short host byte order: 53764, Network byte order: 53764
设定并获取默认的套接字超时时间
import socket
def test_socket_timeout():
s = socket.socket()
# gettimeout()获取套接字超时时间
print("Default socket timeout: %s" % s.gettimeout())
s.settimeout(100) #修改超时时间 (参数可以是秒数,非负浮点数,也可以是None)
print("Current socket timeout: %s" % s.gettimeout())
if __name__ == "__main__":
test_socket_timeout()
# Default socket timeout: None
# Current socket timeout: 100.0
修改套接字发送和接收的缓冲区大小
import socket
SEND_BUF_SIZE = 4096
RECV_BUF_SIZE = 4096
def modify_buff_size():
sock = socket.socket()
# 获取套接字发送的缓存区大小
bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
print("Buffer size [Default]: %d" % bufsize)
# 修改套接字对象属性(参数:level,optname, value;optname是选项名,value是该选项名的值)
sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_SNDBUF,
SEND_BUF_SIZE
)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_RCVBUF,
RECV_BUF_SIZE
)
bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
print("Buffer size [Current]:%d" % bufsize)
if __name__ == "__main__":
modify_buff_size()
# Buffer size [Default]: 8192
# Buffer size [Current]:4096
把套接字改为阻塞或非阻塞模式
默认情况下,TCP套接字处于阻塞模式中。也就是说,除非完成了某项操作,否则不会把控制权交给程序。
import socket
def test_socket_modes():
sock = socket.socket()
# 参数为1把套接字设为阻塞模式,参数为0把套接字设为非阻塞模式
sock.setblocking(1)
sock.settimeout(0.5)
sock.bind(("127.0.0.1", 0))
socket_address = sock.getsockname()
print("在套接字上启动简易服务器: %s" % str(socket_address))
while True:
sock.listen(1)
if __name__ == "__main__":
test_socket_modes()
# 在套接字上启动简易服务器: ('127.0.0.1', 3873)
重用套接字地址
import socket
import sys
def reuse_socket_addr():
sock = socket.socket()
old_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
print("Old sock state: %s" % old_state)
sock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
new_state = sock.getsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR )
print("New sock state: %s" % new_state )
local_porl = 8282
srv = socket.socket()
srv.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind( ("127.0.0.1", local_porl) )
srv.listen()
print("Listening on port: %s" %local_porl)
while True:
try:
connection, addr = srv.accept()
print("Connected by %s: %s" % (addr[0], addr[1]))
except KeyboardInterrupt:
break
except socket.error as e:
print( "%s" % e)
if __name__ == "__main__":
reuse_socket_addr()
# Old sock state: 0
# New sock state: 1
# Listening on port: 8282
# Connected by 127.0.0.1: 4863 用一个客户端连接测试
# Connected by 127.0.0.1: 4864
# Connected by 127.0.0.1: 4865
编写一个简单的回显客户端/服务器应用
服务器端
import socket
server = socket.socket()
server.bind(("127.0.0.1", 9999)) #绑定IP和端口
server.listen(5) #监听
while True:
print("-------正在等待链接-------")
conn, addr = server.accept() #阻塞等待链接
print("--------正在链接中--------")
print(conn)
print(addr)
print("--------已经链接上了-------")
while True:
msg = conn.recv(1024)
if not msg: #如果客户端断开,即conn.recv接收为空,这时就断开,而外循环会等待下一个连接
print("client has lost...")
break
print("客户端发来的信息:", msg.decode())
conn.send(msg.upper()) #发信息给客户端
conn.close()
server.close()
客户端
import socket
client = socket.socket()
client.connect(("127.0.0.1", 9999)) #链接IP和端口
while True:
msg = input("输入你要发送的信息>>>:")
if (len( msg ) == 0): continue #如果用户直接回车发送空值,那就跳过本次循环
client.send(msg.encode("utf-8")) #向服务器发送信息
data = client.recv(1024) #接收服务器发来的信息,一次接收1K
print("收到服务器发来的信息:", data.decode()) #打印data
client.close() #关闭客户端