1、Socket语法及相关
socket
tcp/ip send recv
udp
family address(地址簇):
AF.INET #ipv4
AF.INET6 #ipv6
AF.UNIX #本地地址,一般用不到
socket protocal type(协议类型):
sock.SOCK_STREAM #tcp/ip(默认)
sock.SOCK_DGRAM #数据报式socket, for udp
服务端:
server = socket.socket(AF_INET,socket.SOCK_STREAM,) #一个实例
server.bind(('localhost',9999)) #监听哪个地址,哪个端口
server.listen() #开始listen
while True: #加一个循环,让客户端断开之后,服务端接收新的客户端链接
conn, addr = server.accept() #每接收一个链接,生成一个客户端实例赋值给conn,addr是那个地址
while True:
print("new conn",addr)
data = conn.recv(1024) #接收数据(接收的大小)赋值给data,####最好是不超过8192(8K)
###conn.recv默认是阻塞的,客户端一断开,conn.recv收到的数据就是空数据,将会进入死循环,So,加个判断
if not data:
barak #如果数据为空,将跳出当前conn.recv,接收新的客户端连接
print(data)
conn.send(data.upper(()) #将数据变成大写返回给客户端
客户端:
client = socket.socket() #实例化一个
client.connect('serverip',9999) #服务端的地址和端口
client.send(data) #发送数据
client.send(data) #可发送多条
client.recv(data) #收取数据
2、socket概念
socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。
我们知道网络 通信 都 是基于 ip+port 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立
对外提供服务,如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司
某个人,必须 先打电话到总机,然后再转分机 。
2.1、简单的实现一下socket
###客户端
#-*- coding:utf-8 -*-
import socket
client = socket.socket() #声明socket类型,同时生成socket连接对象
client.connect(('localhost',6969))
# s=bytes("Hello World",encoding='utf-8')
# client.send(s)
# client.send(b"Hello World")
while True:
msg = input(">>:").strip()
client.send(msg.encode("utf-8"))
data = client.recv(1024)
print("recv",data.decode())
client.close()
###服务端
import socket
server = socket.socket()
server.bind(('localhost',6969)) #绑定要监听的端口
server.listen() #监听端口
print("开始等电话")
while True:
conn, addr = server.accept() # 等电话进来
# conn是客户端连过来在服务器端为其生成的一个链接实例
# print(conn,addr)
print("电话来了")
while True:
data = conn.recv(1024)
print("recv:",data.decode())
if not data:
print("client is lost...")
break
conn.send(data.upper())
server.close()
2.2、实现一个socket相互通信的ssh式的交互
- 服务端
#-*- coding:utf-8 -*-
# Author: li Shang
import socket,os,time
server = socket.socket()
server.bind(('0.0.0.0',9999))
server.listen()
while True: #为了循环接收,客户端挂掉一个,服务端继续的等待别的
conn,addr = server.accept()
print("new conn:",addr)
while True: #循环地接收一个连接发过来的数据
print("开始等待指令")
data = conn.recv(1024)
if not data:
print("客户端已断开")
break
print("执行指令:",data)
cmd_res = os.popen(data.decode()).read() #接收字符串,执行结果也是字符串
print("send brfore")
if len(cmd_res) == 0:
cmd_res = "sorry"
conn.send(str(len(cmd_res.encode("utf-8"))).encode("utf-8"))
# time.sleep(0.5) ###分开两个send,防止粘包,这种方式太low,换成下面的方式
client_ack = conn.recv(1024) #wait client to 确认
print("准备好了吗?客户端:",client_ack.decode())
conn.send(cmd_res.encode("utf-8"))
print("send down")
server.close()
- 客户端
#-*- coding:utf-8 -*-
# Author: li Shang
import socket
client = socket.socket()
client.connect(('localhost',9999))
while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
client.send(cmd.encode(encoding='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,所以必须用len()判断
# print(data.decode("utf-8"))
print(received_size)
received_data += data
else:
print("接收完了...",received_size)
print(received_data.decode("utf-8")) #最后接收完毕,统一输出
client.close()
2.3、终极版(验证MD5):实现一个socket相互通信的FTP式的交互
(用再次与client交互一次来防止粘包)
- 服务端
#-*- coding:utf-8 -*-
# Author: li Shang
import socket,os
import hashlib
server = socket.socket()
server.bind(('0.0.0.0',9999))
server.listen()
while True:
conn,addr = server.accept()
print("new conn:",addr)
while True:
print("开始等待指令")
data = conn.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
conn.send(str(file_size).encode()) #发送文件大小给client
conn.recv(1024) #等待client响应
for line in f:
m.update(line)
conn.send(line)
f.close()
client_ack = conn.recv(1024) #防止粘包,将MD5值也发送过去,等待客户端再次确认
print("准备好了吗?客户端:",client_ack.decode())
conn.send(m.hexdigest().encode()) #然后再发送 MD5 值给客户端
print("file md5:",m.hexdigest())
server.close()
- 客户端
#-*- coding:utf-8 -*-
# Author: li Shang
import socket
import hashlib
client = socket.socket()
client.connect(('10.10.1.19',9999))
while True:
cmd = input(">>:").strip()
if len(cmd) == 0: continue
if cmd.startswith("get"):
client.send(cmd.encode())
server_response = client.recv(1024) #接收server端发来的文件大小
print("server_response:",server_response)
client.send(b"reday 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: #接收到的大小比总大小小,就一直接收
data = client.recv(1024) #接收数据
received_size += len(data) #计算接收的文件的大小
m.update(data)
f.write(data) #把接收的内容写进新文件
print(file_total_size,received_size)
else: #到这就全部接收完了
new_file_md5 = m.hexdigest() #本地文件的MD5值
print("file recv down")
f.close()
client.send("准备好接收了,发吧".encode("utf-8")) #给server端发确认消息,让那边发MD5值过来
server_file_md5 = client.recv(1024) #接收到的server端文件的MD5值
print("client_file_md5:",new_file_md5)
print("server_file_md5:",server_file_md5)
client.close()
2.4、终极终极版(验证MD5):实现一个socket相互通信的FTP式的交互
(服务端不改动,在client端设置最后一次仅接收剩下的数据来防止粘包)
- 服务端
#-*- coding:utf-8 -*-
# Author: li Shang
import socket,os,time
import hashlib
server = socket.socket()
server.bind(('0.0.0.0',9999))
server.listen()
while True:
conn,addr = server.accept()
print("new conn:",addr)
while True:
print("开始等待指令")
data = conn.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
conn.send(str(file_size).encode()) #发送文件大小给client
conn.recv(1024) #等待client响应
for line in f:
m.update(line)
conn.send(line)
f.close()
#client_ack = conn.recv(1024)
#print("准备好了吗?客户端:",client_ack.decode())
conn.send(m.hexdigest().encode()) #send md5
print("file md5:",m.hexdigest())
server.close()
- 客户端
#-*- coding:utf-8 -*-
# Author: li Shang
import socket
import hashlib
client = socket.socket()
client.connect(('10.10.1.19',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"reday 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:
if file_total_size - received_size > 1024: #要收不止一次
size = 1024
else:
size = file_total_size - received_size
print("最后一次收:",size)
data = client.recv(size)
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 down")
f.close()
server_file_md5 = client.recv(1024)
print("client_file_md5:",new_file_md5,type(new_file_md5))
print("server_file_md5:",server_file_md5,type(server_file_md5))
if str(server_file_md5,"utf-8") == new_file_md5:
print("本地文件与server端的一致")
else:
print("本地文件与server端的一致,请检查")
client.close()
3、SocketServer(可以支持并发处理)
class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
server.handle_request() #只处理一个请求
serve_forever() #处理多个请求,永远执行
- 服务端
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler): #定义一个类,继承socketserver.BaseRequestHandler
"""
The request handler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self): #要重写handle功能
while True: #可以持续接收客户端发的信息
try: #与下面你的except配合使用抓住错误
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# if not self.data: #客户端断了,这是原来的用法,这里不用该方法
# print("client is down")
# break
# just send back the same data, but upper-cased
self.request.send(self.data.upper())
except ConnectionResetError as e: #与上面的try配合使用,抓住错误
print("客户端断开了",e)
break
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
- 客户端
import socket
client = socket.socket() #声明socket类型,同时生成socket连接对象
#client.connect(('10.10.1.19',9999))
client.connect(('localhost',9999))
while True: #可以不断地发送数据
msg = input(">>:").strip()
if len(msg) == 0: continue
client.send(msg.encode("utf-8"))
data = client.recv(1024)
print("recv:",data.decode())
client.close()
而服务端只需要将
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) 换成
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) 就可以支持多并发了
4、开发一个支持多用户在线的FTP程序
- 服务端
import socketserver
import json,os
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def put(self,*args):
'''接收客户端文件'''
cmd_dic = args[0]
filename = cmd_dic["filename"]
filesize = cmd_dic["filesize"]
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.request is the TCP socket connected to the client
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):
func = getattr(self,action)
func(cmd_dic)
except ConnectionResetError as e:
print("客户端断开了",e)
break
if __name__ == "__main__":
HOST, PORT = "localhost",9999
# Create the server, binding to localhost on port 9999
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
- 客户端
import socket
import os
import json
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):
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,
"filesize":filesize
}
self.client.send(json.dumps(msg_dic).encode("utf-8"))
server_response = self.client.recv(1024)
print(server_response)
f = open(filename,'rb')
for line in f:
self.client.send(line)
else:
print("文件传送完毕")
else:
print(filename ,"is not exist")
def cmd_get(self):
pass
ftp = FtpClient()
ftp.connect("localhost",9999)
ftp.interactive()