Part.1 简单的socket单次数据传输

服务端:

 #服务器端
import socket server = socket.socket() # 声明socket类型,同时生成socket连接对象
server.bind(('localhost',9999)) # 绑定地址端口,#特别注意,这里面是一个元组,第一次写可能会漏掉一个括号导致报错
server.listen() # 监听 conn,addr = server.accept() # conn是客户端连接过来在服务器端为其生成的一个连接实例(阻塞)
data = conn.recv(1024) # 接收数据,最大1024(阻塞)
print("recv:",data)
conn.send(data.upper()) # 将接收的数据变成大写,发送回去 server.close() # 关闭连接

客户端:

 #客户端
import socket client = socket.socket() # 声明socket类型,同时生成socket连接对象
client.connect(('localhost',9999)) # 特别注意,这里面是一个元组,第一次写可能会漏掉一个括号导致报错 client.send(b"hello world") # 发送数据
data = client.recv(1024) # 接收数据,最大1024(阻塞)
print("recv:",data) client.close() # 关闭连接

Part.2 socket数据多次发送

服务端:

 #实现一个连接的连续往返通话
import socket server = socket.socket()
server.bind(('localhost',9999))
server.listen()
print("等待消息......") conn,addr = server.accept() #等待新的客户端过来
while True: # 循环是实现同一个连接内,可以发送多次消息,而不是发送一次就退出了
data = conn.recv(1024)
print("recv:",data.decode())
# 在windows上面断开客户端,服务端也会终止服务,而在Linux或者mac上断开客户端,这里就接收空数据,并不会断开,一直死循环接收空
# 所以下面判断传来的数据是否为空
if not data:
print(client has lost...)
break
conn.send(data.upper()) server.close()

客户端:

 import socket

 client = socket.socket()
client.connect(('localhost',9999)) while True:
msg = input(">>:").strip() # strip方法是去掉两头的空格或者指定字符串
if len(msg) == 0:continue # 这里不能send空(输入时直接回车),不然客户端就会卡住,所以要加一个判断
client.send(msg.encode()) # encode转换为byte,如果是单纯的字符串,前面加b
data = client.recv(1024)
print('recv:',data.decode()) # decode与encode相反 client.close()

Part.3 socket服务端执行返回客户端的输入命令

服务端:

 import socket
import os server = socket.socket()
server.bind(('localhost',9999))
server.listen() conn,addr = server.accept()
while True:
data = conn.recv(1024)
print("recv:",data)
if not data:
print("client had closed......")
break
res = os.popen(data.decode()).read() # 或者decode("utf-8")
# res = os.popen(data).read() 如果执行命令后得到的结果有中文,这样写会报错
conn.send(res.encode()) # 或者encode("utf-8")
# conn.send(res) 如果执行命令后得到的结果有中文,这样写会报错 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())
res = client.recv(1024)
print(res.decode()) client.close()

Part.4 客户端断开连接,服务端也能等待下一个连接

服务端:

 import socket

 server = socket.socket()
server.bind(('localhost',9999))
server.listen() while True: # 此循环实现,当目前连接的一个客户端断开连接时,服务端能重新等待第二个客户端的连接,而不是关闭客户端就导致服务端也关闭了
conn,addr = server.accept()
print("建立连接,等待消息。。。")
while True:
data = conn.recv(1024)
if not data:break
print("recv:",data.decode())
conn.send(data.upper()) server.close()

Part.5 socket处理一次客户请求返回大于1024字节的数据

服务端:

 import socket
import os server = socket.socket()
server.bind(('localhost',9999))
server.listen() while True:
conn,addr = server.accept()
print("new conn:",addr)
while True:
data = conn.recv(1024)
if not data:
print("client has lost...")
break
print("excution cmd:",data)
res = os.popen(data.decode()).read()
if len(res) == 0: # res长度为0,说明传过来的命令是错误的,不能返回正确的结果,所以这里做判断
res = data.decode() + ": command not found"
conn.send(str(len(res.encode())).encode()) # 发送命令结果大小
a = conn.recv(1024) # 上下两个send在一块时,会发生粘包,缓冲区将两次发送的数据粘在一块发送,所以这里就设置一个接收消息来分割两次发送
# conn.send(str(len(res)).encode()) # 这里的res是命令返回结果,是字符串,这里len针对中文和byte的长度返回值是不同的,所以要转换为byte再len
conn.send(res.encode()) 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())
res_size_byte = client.recv(1024)
client.send(b'') # 防止粘包而设置的发送
res_size = int(res_size_byte.decode()) # 这是接收到的服务端发来的命令结果总大小
recvd_size = 0 #recvd_size是已经接收了的大小值
while recvd_size != res_size: #判断目前接收了命令结果的大小是否与服务端发来的结果总大小相同,相同代表接收完了
data = client.recv(1024)
recvd_size += len(data) # 每次收到的有可能小于1024,所以必须用len判断
print(data.decode()) client.close()

Part.6 socket实现简单的文件下载功能

服务端:

 import socket
import os
# import hashlib server = socket.socket()
server.bind(('localhost',9999))
server.listen() while True:
conn,addr = server.accept()
print("new conn:",addr)
while True:
cmd_file = conn.recv(1024)
if not cmd_file: continue
cmd,filename = cmd_file.decode().split() # 客户端下载文件命令一般是 命令+文件,所以使用split分割出命令和文件
if os.path.isfile(filename): # 判断这个是否是文件,可能不存在,也可能是目录
f = open(filename,"rb")
# m = hashlib.md5()
file_size = os.stat(filename).st_size # os.stat是可以获取文件的详细信息,其中st_size表示文件的大小
conn.send(str(file_size).encode()) # 将文件大小发送给客户端
conn.recv(1024) # 等待客户端确认,防止粘包
for line in f:
# m.update(line)
conn.send(line)
f.close() server.close()

客户端:

 import socket

 client = socket.socket()
client.connect(('localhost',9999)) while True:
cmd = input(">>>:").strip()
if len(cmd) == 0: continue
if cmd.startswith("get"):
filename = cmd.split()[1] # 获取文件名
client.send(cmd.encode())
file_size_byte = client.recv(1024) # 接收文件大小
client.send(b"ready") # 防止粘包
file_size = int(file_size_byte.decode())
recvd_size = 0 # 传输接收到了的文件大小
f = open(filename,"wb")
while recvd_size < file_size: # 传输文件,当传输到最后一次的时候数据大小不一定是1024,所以会发生粘包,因此下面会判断最后一次剩余大小做处理
if file_size - recvd_size > 1024:
size = 1024
else:
size = file_size -recvd_size
data = client.recv(size) # 如果传输的时候不是最后一次,那么size每次都是1024,如果是最后一次,size就是最后剩余大小
recvd_size += len(data)
f.write(data)
else:
print("file transfer finished")
f.close() client.close()

Part.7 socketserver对socket的再次封装

服务端:

 import socketserver

 class MyTCPHandler(socketserver.BaseRequestHandler):

     def handle(self):  # 服务端跟客户端所有的交互都写在这个handle函数里
while True: # 可以多次接收客户端的数据进行处理,而不是进行一次就完了
try:
self.data = self.request.recv(1024).strip() # 接收数据
# if not self.data: break
print("{} wrote:".format(self.client_address[0]))
print(self.data)
self.request.send(self.data.upper())
except ConnectionResetError as e: # 出现这个错误表示客户端已断开,上面的判断数据是否为空 没用
print("err:",e)
break if __name__ == "__main__":
HOST,POST = "localhost",9999
# server = socketserver.TCPServer((HOST,POST),MyTCPHandler) # 一个服务端处理一个客户端,单线程
server = socketserver.ThreadingTCPServer((HOST, POST), MyTCPHandler) # 一个服务端处理多个客户端,多线程
server.serve_forever()

eg:实现一个简单的ftp

  1.用户登录和注册,用户登录后才能使用

  2.每个用户有自己的家目录

  3.可以查看当前家目录下的文件

  4.用户上传文件到家目录

  5.用户从家目录下载文件

  6.在上传和下载文件的时候,显示进度条

  7.不断开客户端情况下,退出当前用户,可以登录其他用户

#服务端
ftp
- main
- server.py #代码程序文件
- conf
- user #存储用户名和MD5加密密码的文件
-home
- dev #dev用户的家目录
- admin
- user1
- user2

目录结构

# 100 防止粘包
# 404 文件未找到
# 101 登录成功
# 102 密码错误
# 103 用户不存在
# 104 注册成功
# 105 用户已存在
# 130 目录下没有文件

定义的状态码

dev$e77989ed21758e78331b20e477fc5582
#用户名和加密的密码之间用$分隔开

user格式

import socketserver
import json
import os class MyTCPHandler(socketserver.BaseRequestHandler): def put(self,*args):
'''接收客户端文件'''
file_dic = args[0]
filename = file_dic["filename"]
filesize = file_dic["filesize"]
fileuser = file_dic["fileuser"]
file_path = "../home/" + fileuser + "/" + filename
if os.path.isfile(file_path): # 判断文件是否存在
f = open(file_path + ".new","wb")
else:
f = open(file_path,"wb")
self.request.send(b"") # 防止粘包
recvd_size = 0
while recvd_size < filesize: # 判断文件已经接收的大小和文件总大小
data = self.request.recv(1024)
f.write(data)
recvd_size += len(data)
else:
f.close()
print("file has uploaded") def get(self,*args):
"""发送文件到客户端"""
file_dic = args[0]
filename = file_dic["filename"]
fileuser = file_dic["fileuser"]
file_path = "../home/" + fileuser + "/" + filename
if os.path.isfile(file_path): # 判断文件是否存在
file_size = os.stat(file_path).st_size # 获取文件的大小
self.request.send(str(file_size).encode()) # 发送文件大小给客户端
self.request.recv(1024)
f = open(file_path,"rb")
for line in f:
self.request.send(line)
else:
f.close()
print("file download finish")
else:
self.request.send(b"404 none") # 文件不存在 def login(self,*args):
"""用户登录,用接收到的用户名和密码去和user文件遍历验证"""
user_dic = args[0]
username = user_dic["username"]
password = user_dic["password"]
f = open(r"../conf/user","r")
for line in f:
line = line.strip()
line_list = line.split("$")
# print(line_list[0],username)
# print(line_list[1],password)
if line_list[0] == username and line_list[1] == password:
self.request.send(b"") # 101 登录成功
return
elif line_list[0] == username and line_list[1] != password:
self.request.send(b"") # 102 密码错误
f.close()
return
else:
self.request.send(b"") # 103 用户不存在
f.close() def regist(self,*args):
"""注册用户,主要是接收到传来的用户名和密码,将其写入到user文件中"""
user_dic = args[0]
username = user_dic["username"]
password = user_dic["password"]
f = open(r"../conf/user","r+") # r+ 读写,可以先检查是否存在
for line in f:
line = line.strip()
line_list = line.split("$")
if line_list[0] == username:
f.close()
self.request.send(b"")
return
temp = "\n" + username + "$" + password # 加上 \n 换行符,不然所有的用户都在一行
f.write(temp)
f.close()
os.mkdir("../home/{}".format(username)) # 在注册的同时就创建好相应的家目录
self.request.send(b"") def ls(self,*args):
"""查看指定目录下文件,并返回相应的文件列表"""
username = args[0]["username"]
data = os.listdir("../home/" + username) # 列出指定目录下的所有文件
if len(data) == 0:
self.request.send(b"130 none")
else:
self.request.send(json.dumps(data).encode()) def handle(self):
print("{} wrote:".format(self.client_address[0]))
while True:
try:
self.data = self.request.recv(1024).strip()
file_dic = json.loads(self.data.decode()) # loads将字符串类型转化为字典类型(数据传输只能是byte类型,字典必须事先用json转)
action = file_dic["action"]
if hasattr(self,action): # 这里使用反射,对客户端传来的数据action,告诉服务端该操作什么
func = getattr(self,action)
func(file_dic)
except ConnectionResetError as e:
print("err:",e)
break if __name__ == "__main__":
HOST,POST = "localhost",9998
server = socketserver.ThreadingTCPServer((HOST, POST), MyTCPHandler) # 一个服务端处理多个客户端,多线程
server.serve_forever()

server.py

import socket
import os
import json
import hashlib user = "none" # 当用户成功登录后,就会改变这个全局变量为当前用户名
class FtpClient(object): def __init__(self):
self.client = socket.socket() def connection(self,ip,port):
"""
客户端去连接服务端
:param ip: 服务端IP地址
:param port: 服务端端口号
"""
self.client.connect((ip,port)) def login(self):
"""
用户登录验证
:return:登录成功返回True,登录失败返回False
"""
m = hashlib.md5() # 设定加密类型
username = input("[login]<username>:").strip()
passw = input("[login]<password>:").strip()
m.update(bytes(passw,encoding="utf-8")) # 对密码进行加密
password = m.hexdigest() # 返回十六进制的加密后的密码,加密时必须对bytes类型数据加密,使用bytes时必须指定encoding
user_dic = {
"username":username,
"password":password, # 这里的密码已经是加密了的
"action":"login"
}
user_dic_byte = str(json.dumps(user_dic)).encode() # 将这个字典转化为bytes类型,传给服务端
self.client.send(user_dic_byte)
login_res_byte = self.client.recv(1024) # 返回用户验证后的状态码(用户名或者密码对不对)
login_res = login_res_byte.decode()
if login_res == "": # 用户名和密码正确
global user
user = username
print("Login successed")
return True
elif login_res == "": # 用户存在,密码错误
print("password error")
return False
elif login_res == "": # 用户不存在
print("user {} does not exist".format(username))
return False def regist(self):
"""用户注册"""
m = hashlib.md5()
username = input("[regist]<username>:").strip()
passw = input("[regist]<password>:").strip()
m.update(bytes(passw,encoding="utf-8")) # 加密时必须对bytes类型数据加密,使用bytes时必须指定encoding
password = m.hexdigest() # 返回十六进制的加密后的密码
user_dic = {
"username": username,
"password": password, # 这里的密码已经是加密了的
"action": "regist"
}
user_dic_byte = str(json.dumps(user_dic)).encode()
self.client.send(user_dic_byte)
regist_res_byte = self.client.recv(1024)
regist_res = regist_res_byte.decode()
if regist_res == "": # 用户注册成功
print("Regist successed")
return False
elif regist_res == "": # 用户已经存在
print("user {} exist".format(username))
return False def userlogin(self):
"""实现用户登录或注册的交互"""
while True:
option = input("<login or regist>(L/l or G/g):").strip()
if option.upper() == "L" and self.login():
break
elif option.upper() == "G":
self.regist()
else:
continue
self.interact() def interact(self):
"""
实行客户端与服务端的交互
"""
while True:
opt = input("[{}@ftpserver ~]# ".format(user)).strip()
if len(opt) == 0: continue
cmd = opt.split()[0] # 取出命令
if hasattr(self,cmd): # 通过反射,执行相应的函数(一个命令对应一个函数,服务端也是如此)
func = getattr(self,cmd)
func(opt)
else:
self.help() def help(self):
"""
命令使用帮助信息
"""
msg = """命令使用帮助:
ls 查看家目录下的文件目录
put file 上传文件
get file 下载文件
exit 退出当前用户
"""
print(msg) def progress(self,passed,all):
"""
进度条显示
:param passed: 已经进行过的长度
:param all: 进度条总长度
"""
s = passed * 100 // all
a = s * "#"
b = (100 - s) * "-"
print('\r %d%% [%s%s]' % (s, a, b), end="") # 光标回到行首,两个百分号表示输出一个百分号,end默认为\n,意为换行,改为空格就不换行 def put(self,*args):
"""
客户端上传文件
"""
cmd_list = args[0].split() # 将命令和文件名分开
if len(cmd_list) > 1: # 判断输入的命令是否正确,列表里命令加上文件名至少是两个元素
filename = cmd_list[1]
if os.path.isfile(filename): # 判断文件是否存在
filesize = os.stat(filename).st_size # 获取文件的大小
file_dic = {
"filename":filename,
"filesize":filesize,
"action":"put", # action是为了告诉服务端该执行什么操作
"fileuser":user # 传这个值是因为要告诉服务端把文件放到哪个用户的家目录
}
self.client.send(json.dumps(file_dic).encode())
responce = self.client.recv(1024) # 防止粘包,后续可用作验证返回
f = open(filename,"rb")
recvd_size = 0
for line in f:
recvd_size += len(line)
self.progress(recvd_size,filesize) # 进度条函数,传入已传文件大小和总文件大小
self.client.send(line)
else:
print("\nfile upload finish")
f.close()
else:
print("'" + filename + "': No such file") def get(self,*args):
"""
客户端下载文件
"""
cmd_list = args[0].split()
if len(cmd_list) > 1:
filename = cmd_list[1]
file_dic = {
"filename":filename,
"action":"get",
"fileuser": user
}
self.client.send(json.dumps(file_dic).encode())
file_size_byte = self.client.recv(1024) # 传回的要么是文件大小,要么是状态码,下面就做判断
if file_size_byte.decode() != "404 none":
file_size = int(file_size_byte.decode())
self.client.send(b"")
recvd_size = 0
if os.path.isfile(filename):
f = open(filename + ".new","wb")
else:
f = open(filename,"wb")
while recvd_size < file_size: # 判断已接收的文件大小和文件总大小
data = self.client.recv(1024)
f.write(data)
recvd_size += len(data)
self.progress(recvd_size,file_size) # 进度条
else:
f.close()
print("\nfile has download")
else:
print("file no exist") def exit(self,*args):
# 退出当前用户
self.userlogin() def ls(self,*args):
"""查看用户家目录下的所有文件"""
ls_dic = {
"username":user,
"action":"ls",
}
self.client.send(json.dumps(ls_dic).encode())
data = self.client.recv(1024)
if data.decode() != "130 none": # 要么返回列表,要么返回状态码
ls_list = json.loads(data.decode())
for i in ls_list:
print(i) ftp = FtpClient()
ftp.connection("localhost",9998)
ftp.userlogin()

client.py

python入门之socket代码练习的更多相关文章

  1. Python入门基础:代码的编码风格

    每种语言都有自己的编码风格,对于Python这种比较注重于空格的影响的代码而言,其风格也是相当重要的. 主要包括以下几点: 1:使用 4 空格缩进,而非 TAB  .在小缩进(可以嵌套更深)和大缩进( ...

  2. Python入门-第一行代码到多行代码

    不管学啥语言,开始的第一行代码都是: print("hello word") 回车之后,就代表你正式进入代码的世界! 如果报错,恭喜你获得第一个书写bug,请检查单词拼写,双引号, ...

  3. python入门(5)使用文件编辑器编写代码并保存执行

    python入门(5)使用文件编辑器编写代码并保存执行 两款文本编辑器: 一个是Sublime Text,免费使用,但是不付费会弹出提示框: 一个是Notepad++,免费使用,有中文界面: 请注意, ...

  4. 2018-06-21 中文代码示例视频演示Python入门教程第五章 数据结构

    知乎原链 续前作: 中文代码示例视频演示Python入门教程第四章 控制流 对应在线文档: 5. Data Structures 这一章起初还是采取了尽量与原例程相近的汉化方式, 但有些语义较偏(如T ...

  5. 2018-06-20 中文代码示例视频演示Python入门教程第四章 控制流

    知乎原链 续前作: 中文代码示例视频演示Python入门教程第三章 简介Python 对应在线文档: 4. More Control Flow Tools 录制中出了不少岔子. 另外, 输入法确实是一 ...

  6. 2018-06-20 中文代码示例视频演示Python入门教程第三章 简介Python

    知乎原链 Python 3.6.5官方入门教程中示例代码汉化后演示 对应在线文档: 3. An Informal Introduction to Python 不知如何合集, 请指教. 中文代码示例P ...

  7. Python 入门之代码块、小数据池 与 深浅拷贝

    Python 入门之代码块.小数据池 与 深浅拷贝 1.代码块 (1)一个py文件,一个函数,一个模块,终端中的每一行都是代码块 (代码块是防止我们频繁的开空间降低效率设计的,当我们定一个变量需要开辟 ...

  8. 25 【python入门指南】如何编写测试代码

    python如何编写测试代码 python内置了unittest,使得写应用层的单元测试变得超乎寻常的简单. 1,执行单个测试函数 #!/bin/python import unittest clas ...

  9. 简述Python入门小知识

    如今的Python开发工程师很受企业和朋友们的青睐,现在学习Python开发的小伙伴也很多,本篇文章就和大家探讨一下Python入门小知识都有哪些. 扣丁学堂简述Python入门小知识Python培训 ...

随机推荐

  1. backbonejs学习

    文章: http://www.cnblogs.com/yexiaochai/archive/2013/07/27/3219402.html http://blog.csdn.net/cony100/a ...

  2. JavaScript编写代码时候没有提示

    如上图所示如果没有提示可能是箭头所指示的类型不正确.

  3. 删除 char[10][10] 中的一行

    1. 描述 删除二维字符数组其中一行,并用下一行进行填补 2. 代码 #include <iostream> #include <string.h> using namespa ...

  4. Django框架之第三篇模板语法

    一.什么是模板? 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法分类 一.模板语法之变量:语法为 {{ }}: 在 Django 模板中遍历复杂数据结构的关键 ...

  5. WPF 导出Excel(合并单元格)

    WPF 导出Excel(合并单元格) DataTable 导出Excel(导出想要的列,不想要的去掉) ,B1,B2,B3,B4,B5} MisroSoft.Office.Interop.Excel. ...

  6. mongodb 修改操作

    $addToSet与$each结合完成批量数组更新 db.text.update({_id:1000},{$addToSet:{books:{$each:["js","d ...

  7. Windows 8 64位系统 在VS2010 32位软件上 搭建 PCL点云库 开发环境

    Windows 8 64位系统 在VS2010 32位软件上 搭建 PCL点云库 开发环境 下载PCL For windows 软件包 到这个网站下载PCL-All-In-One Installer: ...

  8. Jmeter连接MySQL配置(能执行多条sql语句)

    Database URL为MySQL的连接串,如果要执行多条SQL语句,后面还要添加“?allowMultiQueries=true”

  9. storm操作zookeeper源码分析-cluster.clj

    storm操作zookeeper的主要函数都定义在命名空间backtype.storm.cluster中(即cluster.clj文件中).backtype.storm.cluster定义了两个重要p ...

  10. 【转】eclipse中window->preference选项中没有tomcat的解决方法

    eclipse中window->preference选项中没有tomcat的解决方法 2011-09-09 13:46:35|  分类: eclipse|字号 订阅 其实一共有好几种方法,这只是 ...