一、Socket是什么
-
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设 计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
-
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
二、UDP(不堵塞)
###### Server ######
from socket import *
ip_port = ("127.0.0.1", 60008)
buffer_size = 1024
socobj = socket(AF_INET, SOCK_DGRAM)
socobj.bind(ip_port)
while True:
try:
data, addr = socobj.recvfrom(buffer_size)
if not data: break
print(data, addr)
socobj.sendto(data.upper(), addr)
except Exception as e:
print(e)
break
###### Client ######
from socket import *
ip_port = ("127.0.0.1", 60008)
buffer_size = 1024
socobj = socket(AF_INET, SOCK_DGRAM)
while True:
data = input("Lion>>:").strip().encode()
socobj.sendto(data, ip_port)
data, addr= socobj.recvfrom(1024)
print(data)
二、socketserver TCP多线程服务端(不堵塞)
###### Server ######
import socketserver
ip_port = ("127.0.0.1", 60007)
buffer_size = 1024
class MyServer(socketserver.BaseRequestHandler):
def handle(self): # 重写handle方法
print(self.request) # self.request == connect
print("--已经连接--", self.client_address) # self.client_address == address
while True:
try:
data = self.request.recv(buffer_size)
if not data: break
print(data, self.client_address)
self.request.send(data.upper())
except Exception as e:
print(e)
break
self.request.close()
if __name__ == "__main__":
s = socketserver.ThreadingTCPServer(ip_port, MyServer) # 开启新线程 并 运行MyServer实例handle方法
s.serve_forever() # 一直循环开启服务器接受客户端消息
###### Client ######
from socket import *
ip_port = ("127.0.0.1", 60007)
buffer_size = 1024
socobj = socket(AF_INET, SOCK_STREAM)
socobj.connect(ip_port)
while True:
try:
data = input("Lion>>:").strip().encode("utf-8")
if not data: continue
if data == "quit": break
socobj.send(data)
data = socobj.recv(buffer_size)
print(data.decode("utf-8"))
except Exception as e:
print(e)
break
socobj.close()
一、验证客户端链接合法性(socketserver多线程)
###### Server ######
# from socket import *
import socketserver, os, hmac
"""
Digest 采用数字签名标准之一的RSA数字签名算法,通过先对原文进行单向哈希 (Hash)运算,
生成该文512bit的文摘(Digest),然后再对文摘用RSA算法生成数字签名,大大提高了对经过审
核被批准的公文进行数字签名的速度,使得流程系统具有对用户身份认证和...
"""
ip_port = ("127.0.0.1", 60009)
buffer_size = 1024
secret_key = b"I am the king!"
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print("-已经连接-", self.client_address)
if not self.outhoriz_request():
print("-连接不合法-")
return
print("-连接合法 开始通信-")
while True:
try:
data = self.request.recv(buffer_size)
if not data: break
print("server got:", data)
self.request.send(data.upper())
except Exception as e:
print(e)
break
def outhoriz_request(self):
urandom = os.urandom(32) # 返回32个包含适合加密使用的随机字节的字节对象。
self.request.send(urandom)
my_hash = hmac.new(secret_key, urandom) # 创建一个新的散列对象(hashing object)并返回它。
digest = my_hash.digest() # 从一个hash object返回一个hash value。
response = self.request.recv(len(digest)) # 收同样长度的hash value
return hmac.compare_digest(response, digest) # 判断是否为同一个哈希值
if __name__ == "__main__":
s = socketserver.ThreadingTCPServer(ip_port, MyServer)
s.serve_forever()
###### Client ######
from socket import *
import hmac
ip_port = ("127.0.0.1", 60009)
buffer_size = 1024
key = b"I am the king!"
def outhoriz_request(socobj):
msg = socobj.recv(32)
my_hash = hmac.new(key, msg)
digest = my_hash.digest()
socobj.send(digest)
def client_handle():
socobj = socket(AF_INET, SOCK_STREAM)
socobj.connect(ip_port)
outhoriz_request(socobj)
while True:
try:
data = input("Lion>>:").strip().encode("utf-8")
if not data: continue
if data == b"quit": break
socobj.send(data)
print("-已经发送-")
backdata = socobj.recv(buffer_size)
print("Client got back data:", backdata.decode("utf-8"))
except Exception as e:
print(e)
break
if __name__ == "__main__":
client_handle()
一、Socket流重定向
- 在server里以“r”的模式打开 socobj.makefile("r") # 不可以用 “wb” 模式
- 在client里以“w”的模式打开 socobj.makefile("w")
- client里写完一条data需要 sys.stdout.flush()
- 通过客户端socobj对象的file来读写内容。
- socobj.makefile("w", 0) 不可以用 “wb” 模式; arg不可以是0,完全无缓冲;arg可以是1,行缓冲;
from socket import *
import time, multiprocessing, os, sys
ip_port = ("127.0.0.1", 60001)
back_log = 5
# ------ in server ------
def server1():
pid = os.getpid()
socobj = socket(AF_INET, SOCK_STREAM)
socobj.bind(ip_port)
socobj.listen(back_log)
conn, addr = socobj.accept()
print(conn, "connnnnnnnn")
file = conn.makefile("r")
for i in range(3):
data = file.readline().strip()
print("server %s got: [%s]" % (pid, data))
def client1():
pid = os.getpid()
socobj = socket(AF_INET, SOCK_STREAM)
socobj.connect(ip_port)
file = socobj.makefile("w")
sys.stdout = file
for i in range(3):
print("pid %s,%s" % (pid, i))
sys.stdout.flush()
time.sleep(1.5)
if __name__ == "__main__":
multiprocessing.Process(target=server1).start()
client1()
一、Socket文本模式文件流和缓冲输出流
-
conn.recv() 也能接收 socobj.makefile("w")对象file的write
-
立刻flush:
- print(file=file, flush=True) 需要加参数"flush=True",就会立刻flush
- file.write("data") 之后一行加 file.flush() 也会立刻flush
- 在Client连接到Server时候,connect在新的Thread里运行。
for i in range(5): # 会在10秒之后for循环结束才打印全部消息;
sys.stdout.write("spam" + str(i))
# sys.stdout.flush() # 除非每条write后面都跟一条 sys.stdout.flush()
time.sleep(2)
- 如果碰到死锁:
- 手动刷新sys.stdout.flush();
- print(flush=True);
- 命令行里加 python -u 强制输出;
- conn.makefile("r", buffering=1) # 逐行flush
- open("filename", "w", buffering=1)
###### Server ######
from socket import *
ip_port = ("127.0.0.1", 60009)
buffer_size = 1024
socobj = socket(AF_INET, SOCK_STREAM)
socobj.bind(ip_port) # bind it to server port number
socobj.listen(5)
conn, addr = socobj.accept()
for i in range(3):
print(i)
data = conn.recv(buffer_size)
print(data)
###### Client ######
from _socket import SOCK_STREAM
from socket import *
import time
ip_port = ("127.0.0.1", 60009)
buffer_size = 1024
socobj = socket(AF_INET, SOCK_STREAM)
socobj.connect(ip_port)
file = socobj.makefile("w")
for i in range(3):
time.sleep(0.5)
file.write("spam"+str(i))
print("egg1", file=file)
socobj.send(b"ham1") # send会比file.write接收到的快;
网友评论