UDP Socket
UDP Client
import socket
# AF_INET是 IPv4 网络协议的套接字类型,AF_INET6 则是 IPv6 的;而 AF_UNIX 则是 Unix 系统本地通信。
# socket.SOCK_DGRAM 代表UDP协议
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 发送方如果不绑定端口,操作系统会随机分配一个端口号
udp_socket.bind(("127.0.0.1", 9999))
while True:
msg = input("请输入要发送的内容:")
if msg == "exit":
break
# 需要编码为bytes---encode("utf-8")返回bytes
udp_socket.sendto(msg.encode("utf-8"), ("127.0.0.1", 8888))
udp_socket.close()
UDP Server
import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定本地地址及监听的端口号
address = ("127.0.0.1", 8890)
udp_socket.bind(address)
while True:
try:
# 通过套接字接收数据,这里设定一次最多接收1024个字节
# TODO 如果设置为10,但是发送的数据大于10个字节就会报
# WinError 10040,怎么避免这种问题?
data = udp_socket.recvfrom(1024)
# 因为当前是windows系统,所以解码为gbk,
# 如果是linux这里应该是utf-8(要根据发送方的编码格式解码)
msg = data[0].decode("gbk")
address = data[1]
print("from: %s, msg:%s" % (address, msg))
except Exception as err:
print("Err:%s" % err)
break
udp_socket.close()
有收发功能UDP
注意:发送方发送了数据后,操作系统会缓存起来,接收方需要通过recvfrom来主动接收数据。并且,如果执行了recvfrom但是发送方没有发送数据,程序阻塞。
import socket
# 创建udp socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定接收消息地址
address = ("127.0.0.1", 8888)
udp_socket.bind(address)
while True:
operate = input("请输入要使用的功能:\n0:发送数据\n1:接收数据\n2:退出程序\n")
if operate == "0":
ip = input("请输入接收方的ip地址:")
port = int(input("请输入接收方的端口:"))
msg = input("请输入要发送的消息:")
udp_socket.sendto(msg.encode("gbk"), (ip, port))
print("发送成功!")
elif operate == "1":
recv_msg = udp_socket.recvfrom(1024)
print("receive from:%s ,msg:%s" % (recv_msg[1], recv_msg[0].decode("gbk")))
elif operate == "2":
print("exit!!!")
break
else:
print("input error,please choose 0 or 1 or 2.")
udp_socket.close()
TCP Socket
TCP Client
import socket
# 创建tcp socket(使用socket.SOCK_STREAM),并连接指定address
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.connect(("127.0.0.1", 8890))
# 发送数据给服务器
tcp_socket.send(b"hahaha1111")
# 关闭连接
tcp_socket.close()
TCP Server
注意: 如下程序只能一次连接一个client,必须一个断开后再接受另一个连接。
from datetime import datetime
import socket
# 创建服务器socket,并监听8890端口
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.bind(("127.0.0.1", 8890))
tcp_socket.listen(128) # 同一时刻最多有个128个客户端连接
while True:
print("Ready to accept.Time:%s" % datetime.now())
# 这里会阻塞,直到收到客户端连接
client_socket, client_addr = tcp_socket.accept()
print("Accept client:%s.Time:%s" % (str(client_addr), datetime.now()))
while True:
# 这里会阻塞,直到客户端发送数据
msg = client_socket.recv(1024)
print("Client msg:%s. Time:%s" % (msg.decode("gbk"), datetime.now()))
# 如果客户端关闭,recv将不会解阻塞,并且这里msg=b"",即服务端关闭也需要客户端连接
if msg:
# 服务端响应,并关闭客户端连接
print("Begin response to client.")
client_socket.send(b"server response")
else:
break
client_socket.close()
tcp_socket.close()
TCP下载文件
download_server.py:
import os
import socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.bind(("127.0.0.1", 8891))
tcp_socket.listen(128)
while True:
client_socket, client_addr = tcp_socket.accept()
print("client_addr:%s" % str(client_addr))
file_name = client_socket.recv(1024).decode("utf-8")
data = None
if os.path.exists(file_name):
with open(file_name, "rb") as fp:
data = fp.read()
print("Begin send data.")
client_socket.send(data)
else:
print("file_name invalid.")
client_socket.send(b"Error file_name")
tcp_socket.close()
download_client.py
import socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.connect(("127.0.0.1", 8891))
file_name = "211.txt"
tcp_socket.send(file_name.encode("utf-8"))
data = tcp_socket.recv(1024)
if data and data.decode("utf-8") != "Error file_name":
with open(file_name+".bak", "wb") as fp:
fp.write(data)
else:
print("file_name error.Begin exit.")
tcp_socket.close()
python多线程
python多线程主要用threading模块实现。
使用方法的形式创建多线程
import threading
count = 0
def run_thread():
global count
for i in range(100000):
count += 1
t1 = threading.Thread(target=run_thread)
t2 = threading.Thread(target=run_thread)
t1.start()
t2.start()
t1.join()
t2.join()
print(count)
使用类的形式创建多线程
自定义类继承threading.Thread:
import threading
count = 0
class CountThread(threading.Thread):
def run(self):
global count
for i in range(100000):
count += 1
t1 = CountThread()
t2 = CountThread()
t1.start()
t2.start()
t1.join()
t2.join()
print(count)
互斥锁
如上,线程间共享全局变量,如果多个线程同时操作一个全局变量可能引起异常。
这个问题可以通过互斥锁解决,在访问全局变量时先获取锁对象,操作完后再释放锁对象。
import threading
count = 0
lock = threading.Lock()
class CountThread(threading.Thread):
def run(self):
global count
for i in range(100000):
# 锁范围要尽量小
lock.acquire()
count += 1
lock.release()
t1 = CountThread()
t2 = CountThread()
t1.start()
t2.start()
t1.join()
t2.join()
print(count)
多任务版本的udp消息收发
启动两个线程分别用于接收和发送消息:
import threading
import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind(("127.0.0.1", 8890))
def recv(udp):
while True:
msg = udp.recvfrom(1024)
print("recv from:%s, msg:%s" % (msg[1], msg[0].decode("gbk")))
def send(udp):
while True:
data = input("请输入要发送的内容:")
udp.sendto(data.encode("utf-8"), ("127.0.0.1", 9999))
t1 = threading.Thread(target=recv, args=(udp_socket,))
t2 = threading.Thread(target=send, args=(udp_socket,))
t1.start()
t2.start()