网络通信示意图
socket编程网络步骤图
客户端
import socket
client=socket.socket()#声明socket类型,同时生成socket连接对象
client.connect(("localhost",6969))#连接地址(IP地址和端口)
client.send(b"hello world")#python3只能发byte字节类型
data=client.recv(1024)
print("recv:",data)
client.close()
服务端
import socket
server=socket.socket()#声明类型,使用的是默认类型
server.bind(("localhost",6969))#绑定要监听的端口
server.listen()#监听
print("我要开始等电话了")
#conn就是客户端连过来而在服务器端为其生成的一个连接实例
conn,addr=server.accept()#等电话打进来,conn就是客户端连过来而在服务器端为其生成的一个连接实例,addr链接的地址
print(conn,addr) #('127.0.0.1', 3020)是地址
print("电话来了")
data=conn.recv(1024)
print("recv:",data)
conn.send(data.upper())#发送给客户端
server.close()
能连续发送命令给服务端,服务端给客户端返回执行的命令结果
服务端代码
import socket
import os
import time
server=socket.socket()
server.bind(("localhost",9999))
server.listen()
while True:
coon,addr=server.accept()
print("new conn:",addr)
while True:
print("等待新指令")
data=coon.recv(1024)
if not data:
print("客户端已断开")
break
print("执行指令:",data.decode())
cmd_res=os.popen(data.decode()).read()#接受字符串,执行结果也是字符串
print("before send",len(cmd_res))
if len(cmd_res)==0:
cmd_res="cmd has no output"
'''
下面发送的两个包在一起,就有可能出现粘包的现象,因为缓冲区会把这两条语句当做一条语句发送
'''
coon.send(str(len(cmd_res.encode())).encode("utf-8"))#先发大小给客户端
#time.sleep(0.5)#这样也能解决粘包的问题,但是会产生延时,不适合实时通信的
client_ack=coon.recv(1024)#等待客户端确认,这样就不会粘包了
print("ack from client:",client_ack.decode())
coon.send(cmd_res.encode("utf-8"))
print("send done")
server.close()
客户端
import socket
client=socket.socket()
client.connect(("localhost",9999))
while True:
cmd=input(">>:").strip()
if len(cmd)==0: continue
client.send(cmd.encode("utf-8"))
cmd_res_size=client.recv(1024)#接受命令结果的长度
print("命令结果的大小:",cmd_res_size)
client.send("可以开始接收了".encode("utf-8"))
received_size=0
received_data=b""
while received_size<int(cmd_res_size.decode()):
data=client.recv(1024)
received_size+=len(data)#每次收到的可能小于1024(recv(1024)这个只是说每次最大能接受1024,而不是每次就接受1024,这个值一般最大为8792),所以必须用len判断
print(data.decode())
received_data+=data
else:
print("cmd res receive done...",received_size)
print(received_data.decode())
client.close()
单用户使用get下载文件,并能验证md5值
下载文件步骤
服务端代码
import socket
import os
import hashlib
import time
server=socket.socket()
server.bind(("localhost",9999))
server.listen()
while True:
coon,addr=server.accept()
print("new conn:",addr)
while True:
print("等待新指令")
data=coon.recv(1024)
if not data:
print("客户端已断开")
break
cmd,filename=data.decode().split()
print(filename)
if os.path.isfile(filename):#判断是否为文件
f=open(filename,"rb")
m=hashlib.md5()
file_size=os.stat(filename).st_size#获取文件的大小
coon.send(str(file_size).encode())#发送文件长度
coon.recv(1024)#等待确认
for line in f:
m.update(line)
coon.send(line)
print("file md5",m.hexdigest())
f.close()
coon.send(m.hexdigest().encode())#给客户端发送md5值
print("send done")
server.close()
客户端代码
import socket
import hashlib
client=socket.socket()
client.connect(("localhost",9999))
while True:
cmd=input(">>:").strip()
if len(cmd)==0: continue
if cmd.startswith("get"):
client.send(cmd.encode())
server_response=client.recv(1024)
print("server response:",server_response)
client.send(b"ready to recv file")
file_total_size=int(server_response.decode())
received_size=0
filename=cmd.split()[1]
f=open(filename+".new","wb")
m=hashlib.md5()
while received_size<file_total_size:
'''
通过下面的这个对size的控制可以防止位置1和位置2处发生粘包的发生,因为在接收文件最后不足1024的时候剩多少,我收多少
(粘包发生的原因就是文件收到最后不足1024的时候,可能将md5和文件不足1024的部分一起接收了,但是也有可能不发生粘包,因为每次发送接收的不一定就是1024)
'''
if file_total_size-received_size>1024:#要收不止一次
size=1024
else:
size=file_total_size-received_size
data=client.recv(size)#位置1
received_size+=len(data)
m.update(data)
f.write(data)
print(file_total_size,received_size)
else:
new_file_md5=m.hexdigest()
print("file recv done")
f.close()
server_file_md5=client.recv(1024)#位置2
print("server file md5:",server_file_md5)
print("client file md5:",new_file_md5)
client.close()
* 支持多用户同时上传下载(并发)——socketServer,简例*
创建一个sockerServer的步骤
服务端
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
while True:
try:
self.data=self.request.recv(1024).strip()
print("{} wrote".format(self.client_address[0]))
print(self.data)
# if not self.data:#客户端断了
# print(self.client_address,"断开了")
# break
self.request.send(self.data.upper())
except ConnectionResetError as e:
print("err",e)
break
if __name__== "__main__":
HOST,PORT="localhost",9999
#创建服务端,绑定主机端口9999
server=socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)#多线程并发
#server=socketserver.ForkingTCPServer((HOST,PORT),MyTCPHandler)#多进程并发,在windows上不好使,在Linux上可以,因为windows上的进程不能通过fork实现
server.serve_forever()
客户端
import socket
client=socket.socket()#声明socket类型,同时生成socket连接对象
client.connect(("localhost",9999))#连接地址(IP地址和端口)
while True:
msg=input(">>:").strip()
client.send(msg.encode("utf-8"))#python3只能发byte字节类型
data=client.recv(1024)
print("recv:",data.decode())
client.close()
简易版的ftp并发上传
客户端代码
import socket
import os
import json
client=socket.socket()
client.connect(("localhost",9999))
class FtpClient(object):
def __init__(self):
self.client=socket.socket()
def help(self):
msg='''
ls
pwd
cd ../..
get filename
put filename
'''
print(msg)
def connect(self,ip,port):
self.client.connect((ip,port))
def interactive(self):
#self.authenticate()
while True:
cmd=input(">>").strip()
if len(cmd)==0: continue
cmd_str=cmd.split()[0]
if hasattr(self,"cmd_%s"%cmd_str):#反射
func=getattr(self,"cmd_%s"%cmd_str)
func(cmd)
else:
self.help()
def cmd_put(self,*args):
cmd_split=args[0].split()
if len(cmd_split)>1:
filename=cmd_split[1]
if os.path.isfile(filename):
filesize=os.stat(filename).st_size
msg_dic={
"action":"put",
"filename":filename,
"size":filesize,
"overridden":True
}
self.client.send(json.dumps(msg_dic).encode("utf-8"))
print("send",json.dumps(msg_dic).encode("utf-8"))
#防止粘包,等服务器确认
server_response=self.client.recv(1024)
f=open(filename,"rb")
for line in f:
self.client.send(line)
else:
print("file upload success...")
f.close()
else:
print(filename,"is not exist")
def cmd_get(self):
pass
ftp=FtpClient()
ftp.connect("localhost",9999)
ftp.interactive()
服务端代码
import socketserver
import json
import os
class MyTCPHandler(socketserver.BaseRequestHandler):#括号里面的是固定的
def put(self,*args):
'''接收客户端文件'''
cmd_dic=args[0]
filename=cmd_dic["filename"]
filesize=cmd_dic["size"]
if os.path.isfile(filename):
f=open(filename+".new","wb")
else:
f=open(filename,"wb")
self.request.send(b"200 ok")
received_size=0
while received_size<filesize:
data=self.request.recv(1024)
f.write(data)
received_size+=len(data)
else:
print("file [%s] has uploaded..."%filename)
def handle(self):#这里面的才是跟客户端交互的
while True:
try:
self.data=self.request.recv(1024).strip()
print("{} wrote".format(self.client_address[0]))
print(self.data)
cmd_dic=json.loads(self.data.decode())
action=cmd_dic["action"]
if hasattr(self,action):#判断是否有action所对应的字符串的函数
func=getattr(self,action)#获取action对应的函数
func(cmd_dic)#执行其对应的函数
except ConnectionResetError as e:#捕获异常的
print("err",e)
break
'''
下面是固定的写法
'''
if __name__== "__main__":
HOST,PORT="localhost",9999
#创建服务端,绑定主机端口9999
server=socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
server.serve_forever()