基于TCP套接字
先从服务器端说起。服务器端先初始化
Socket,然后与端口绑定
(bind),对端口进行监听
(listen),调用
accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个
Socket,然后连接服务器
(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
链接过程:三次握手————通信——————四次挥手
TCP服务端
from socket import *
ip_port=('218.197.223.92',800)#本机服务器的地址与端口
back_log=5 #半链池大小
buffer_size=1024 #接收字节数
tcp_serve=socket(AF_INET,SOCK_STREAM)#初始化socket,AF_INET是基于网络模型的套接字家族,SOCK_STREAM是数据流模式
tcp_serve.bind(ip_port) #绑定IP与端口
tcp_serve.listen(back_log) #开始监听
#可以接收多个客户端
while True:
print('开始连接...')
con,addr=tcp_serve.accept() #连接成功后会返回链接与客户端地址
#print(con,addr)
print('连接成功,客户端地址为%s'%(addr[0]))
#与一个客户端持续通信
while True:
try:
msg=con.recv(buffer_size) #recv()读取客户端发送的信息,参数为接收的字节数
print('接收的信息为',msg.decode('utf-8'))
send_msg=input('>>:').strip()
if not send_msg:continue #当发送的信息为空时无法传输,造成客户端无法读取,导致阻塞
con.send(send_msg.encode('utf-8')) #send()发送信息,必须为二进制
except Exception: #当客户端突然断开时会发送错误,用异常处理捕捉错误,让服务器继续运行
print('客户端%s连接断开'%addr[0])
break
tcp_serve.close() #关闭服务器
TCP客户端
from socket import *
ip_port=('218.197.223.92',800) #要连接的服务器的IP与端口
buffer_size=1024 #要读取的字节数
tcp_client=socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port) #连接服务器
#与服务器持续对话
while True:
msg=input('>>:').strip()
if not msg:continue #当msg为空时,数据不会发送出去,服务器也就收不到信息,会导致程序阻塞
tcp_client.send(msg.encode('utf-8'))
msg=tcp_client.recv(buffer_size)
print('收到的信息为',msg.decode('utf-8'))
tcp_client.close() #关闭客户端
远程执行命令的TCP服务端
from socket import *
import subprocess
'''远程执行命令'''
ip_port=('218.197.223.92',800)
back_log=5
buffer_size=1024
tcp_shell_serve=socket(AF_INET,SOCK_STREAM)
tcp_shell_serve.bind(ip_port)
tcp_shell_serve.listen(back_log)
while True:
print('开始连接...')
conn,addr=tcp_shell_serve.accept()
print('连接到客户端:%s'%addr[0])
while True:
try:
cmd=conn.recv(buffer_size)
if not cmd:break #当客户端正常断开时传入为空
res=subprocess.Popen(cmd.decode('utf-8'),shell=True, #运行命令提示符并执行指令 ,并把输出,异常和输入存在subprocess的管道(PIPE)中
stdout=subprocess.PIPE, #输出
stdin=subprocess.PIPE, #输入
stderr=subprocess.PIPE) #异常
err=res.stderr.read() #由管道读出异常
if err: #若存在异常则返回异常
res_cmd=err
else:
res_cmd=res.stdout.read() #否则返回输出结果(输出结果为二进制,编码方式由系统决定,Windowns为gbk编码)
conn.send(res_cmd) #把结果发送给客户端
except Exception: #当客户端非正常断开时会触发异常
print('客户端%s断开连接'%addr[0])
break
tcp_shell_serve.close()
基于UDP套接字
udp是无链接的,先启动哪一端都不会报错(由于udp无连接,所以可以同时多个客户端去跟服务端通信)
UDP服务端
from socket import *
ip_port=('218.197.223.92',800)
buffer_size=1024
udp_serve=socket(AF_INET,SOCK_DGRAM)#创建基于UDP的服务器套接字,SOCK_DGRAM为数据报模式
udp_serve.bind(ip_port) #绑定主机地址与端口
#因为UDP是无连接的,所以不需要TCP中的监听(listen)与链接(accpet)
#与客户端持续通信
while True:
data,addr=udp_serve.recvfrom(buffer_size) #recvfrom()读取客户端发送的信息,返回信息与客户端地址
print('来着客户端地址%s的信息:%s'%(addr,data.decode('utf-8')))
msg=input('>>:').strip() #UDP空字符不会导致阻塞
udp_serve.sendto(msg.encode('utf-8'),addr)#sendto()向客户端发送信息,包括要发送的信息与客户端地址
udp_serve.close()#关闭服务器
UDP客户端
from socket import *
ip_port=('218.197.223.92',800)
buffer_size=1024
udp_client=socket(AF_INET,SOCK_DGRAM)
while True:
msg=input('>>:').strip()
udp_client.sendto(msg.encode('utf-8'),ip_port)
data,addr=udp_client.recvfrom(buffer_size)
print('来着服务端的消息:%s'%(data.decode('utf-8')))
udp_client.close()
模拟一个向服务器发送请求返回时间的UDP服务器
from socket import *
import time
ip_port=('218.197.223.92',800)
buffer_size=1024
udp_serve=socket(AF_INET,SOCK_DGRAM)#创建基于UDP的服务器套接字,SOCK_DGRAM为数据报模式
udp_serve.bind(ip_port) #绑定主机地址与端口
#因为UDP是无连接的,所以不需要TCP中的监听(listen)与链接(accpet)
#与客户端持续通信
while True:
data,addr=udp_serve.recvfrom(buffer_size) #recvfrom()读取客户端发送的信息,返回信息与客户端地址
if not data:
fmt='%Y-%m-%d %X'
else:
fmt=data.decode('utf-8')
udp_serve.sendto(time.strftime(fmt).encode('utf-8'),addr)#sendto()向客户端发送信息,包括要发送的信息与客户端地址
udp_serve.close()#关闭服务器
链接合法验证
服务端
from socket import *
import os,hmac
ip_port=('10.11.165.103',800)
secret_key=b'hello world' #盐
def conn_auth(conn,secret_key):
print('开始验证链接的合法性')
data=os.urandom(32) #产生随机的32位二进制
conn.sendall(data)
h=hmac.new(secret_key,data) #进行哈希运算(加盐)
digest=h.digest()
rec=conn.recv(len(digest))
res=hmac.compare_digest(digest,rec)
if res:
conn.send('1'.encode('utf-8'))
else:
conn.send('0'.encode('utf-8'))
return res
def requst_handler(conn,buffer_size=1024):
if not conn_auth(conn,secret_key):
print('该链接不合法,关闭链接')
conn.close()
return
print('链接验证成功,开始通信')
while True:
data=conn.recv(buffer_size)
print('收到的消息为:',data.decode('utf-8'))
conn.send(data.upper())
def serve_handler(ip_port,back_log=5):
s=socket(AF_INET,SOCK_STREAM)
s.bind(ip_port)
s.listen(back_log)
conn,addr=s.accept()
return requst_handler(conn)
if __name__=='__main__':
serve_handler(ip_port)
客户端
from socket import *
import hmac
ip_port=('10.11.165.103',800)
secret_key=b'hello world'
def auth_conn(conn):
print('开始验证客户端到服务端的链接')
data=conn.recv(32)
h=hmac.new(secret_key,data)
digest=h.digest()
conn.sendall(digest)
res=conn.recv(32)
auth=bool(int(res.decode('utf-8')))
return auth
def data_handler(ip_port,buffer_size=1024):
s=socket(AF_INET,SOCK_STREAM)
s.connect(ip_port)
if not auth_conn(s):
print('验证失败')
return
print('客户端链接到服务端成功')
while True:
msg=input('>>:').strip()
if not msg:continue
s.send(msg.encode('utf-8'))
data=s.recv(buffer_size)
print('收到的消息是',data.decode('utf-8'))
if __name__=='__main__':
data_handler(ip_port)