脱产班第五次大作业-FTP服务器
下载项目
my_ftp
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import hmac
import json
import struct
import hashlib
import socketserver
from core.log import log
class MyServer(socketserver.BaseRequestHandler):
def pro_recv(self):
num = self.request.recv(4)
num = struct.unpack('i', num)[0]
str_dic = self.request.recv(num).decode('utf-8')
dic = json.loads(str_dic)
return dic
def pro_send(self, dic, pro=True):
bytes_dic = json.dumps(dic).encode('utf-8')
if pro:
len_bytes = struct.pack('i', len(bytes_dic))
self.request.send(len_bytes)
self.request.send(bytes_dic)
def get_md5(self, username=0, password=0):
md5 = hashlib.md5(username.encode('utf-8'))
md5.update(password.encode('utf-8'))
return md5.hexdigest()
@staticmethod
def bytes2human(num):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if num >= prefix[s]:
value = float(num) / prefix[s]
return '%.1f%s' % (value, s)
return '%sB' % num
def makencrypt(secret_key):
def outter(func):
def inner(self, *args, **kwargs):
func_ret = func(self, *args, **kwargs)
rand = os.urandom(32)
self.request.send(rand)
h_mac = hmac.new(secret_key.encode('utf-8'), rand)
res = h_mac.digest()
ret = self.request.recv(1024)
if ret == res:
log.logger.info('验证是合法的客户端')
return func_ret
else:
log.logger.info('验证不是合法的客户端')
self.request.close()
return False
return inner
return outter
def checkquota(func):
def wrapper(self, *args, **kwargs):
ret = func(self, *args, **kwargs)
usedsize = self.getusedsize(args[1]['userhome'])
quotasize = args[1]['userquota']
human_used = self.bytes2human(usedsize)
human_total = self.bytes2human(quotasize)
dic = {'used':usedsize,'total':quotasize}
self.pro_send(dic)
if usedsize > quotasize:
log.logger.warning(f'已经超过用户配额:使用{human_used},总共{human_total}')
else:
log.logger.info(f'使用{human_used},总共{human_total}')
return ret
return wrapper
@staticmethod
def getusedsize(path, usedsize=0):
l = [path]
while l:
userhome = l.pop()
lst = os.listdir(userhome)
for name in lst:
son_path = os.path.join(userhome, name)
if os.path.isfile(son_path):
usedsize += os.path.getsize(son_path)
else:
l.append(son_path)
return usedsize
@staticmethod
def sortdic(dic):
for k, v in dic.items():
v.update({'username': k})
return v
@staticmethod
def getfiles(path):
dirlist = list()
for file in os.listdir(path):
if file.startswith('_'):
pass
if file.startswith('.'):
pass
elif os.path.isfile(os.path.join(path, file)):
dirlist.append(('f', file))
elif os.path.isdir(os.path.join(path, file)):
dirlist.append(('d', file))
else:
pass
return dirlist
@makencrypt(secret_key='alexsb')
def login(self, userdic, userfile='userinfo.json', userhome='home'):
log.logger.info(f"用户{userdic['username']}开始登陆")
with open(userfile, encoding='utf-8') as f:
localdic = json.load(f)
if localdic.get(userdic['username']):
if localdic[userdic['username']]['password'] == self.get_md5(
userdic['username'], userdic['password']):
log.logger.info(f"用户{userdic['username']}登陆成功")
msg = f"用户{userdic['username']}登陆成功"
userdic['message'] = msg
userdic['actflag'] = True
self.pro_send(userdic)
return {userdic['username']: localdic[userdic['username']]}
else:
log.logger.warning(f"用户{userdic['username']}登陆的用户名或密码错误,无法登陆")
msg = f"用户{userdic['username']}登陆的用户名或密码错误,无法登陆。"
userdic['message'] = msg
userdic['actflag'] = False
self.pro_send(userdic)
return False
else:
log.logger.warning(f"用户{userdic['username']}不存在,无法登陆")
msg = f"用户{userdic['username']}不存在,无法登陆。"
userdic['message'] = msg
userdic['actflag'] = False
self.pro_send(userdic)
return False
@makencrypt(secret_key='alexsb')
def region(self, userdic, userfile='userinfo.json', userhome='home'):
__default_userquota = 10485760 # 10M
log.logger.info(f"用户{userdic['username']}开始注册")
userhome = os.path.join(userhome, userdic['username'])
if not os.path.exists(userhome):
os.makedirs(userhome)
# check local userinfo
with open(userfile, mode='r', encoding=('utf-8')) as f:
if os.path.getsize(userfile) == 0:
localdic = dict()
else:
localdic = json.load(f)
# check new userinfo
if localdic.get(userdic['username']):
log.logger.warning(f"用户{userdic['username']}已存在,无法注册")
msg = f"用户{userdic['username']}已存在,无法注册。"
userdic['message'] = msg,
userdic['actflag'] = False,
self.pro_send(userdic)
return False
else:
with open(userfile, mode='w', encoding='utf-8') as f:
log.logger.info(f"用户{userdic['username']}注册成功")
msg = f"用户{userdic['username']}注册成功。"
new_userinfo = {
userdic['username']: {
'password': self.get_md5(userdic['username'], userdic['password']),
'userquota': __default_userquota,
'userhome': userhome,
'userdir': userhome,
}}
new_sendinfo = dict()
new_sendinfo['actflag'] = True
new_sendinfo['message'] = msg
localdic.update(new_userinfo)
json.dump(localdic,f,sort_keys=True,indent=2,separators=(',',':'),
ensure_ascii=False)
self.pro_send(new_sendinfo)
return new_userinfo
@checkquota
def upload(self, fileinfo=dict(), userdic=dict()):
filepath = os.path.join(userdic['userdir'], fileinfo['filename'])
break_point = False
if os.path.exists(filepath):
break_point = True
received_size = os.path.getsize(filepath)
if received_size == fileinfo['filesize']:
self.pro_send({'continue':'hasfile'})
return True
else:
self.pro_send({'continue':'send','received':received_size})
else:
self.pro_send({'continue': False})
with open(filepath, 'ab') as f:
md5 = hashlib.md5()
filesize = fileinfo['filesize']
if break_point: filesize -= received_size
while filesize:
content = self.request.recv(2048)
if content == b'': break
md5.update(content)
f.write(content)
filesize -= len(content)
#check md5num
self.pro_send({'md5num': md5.hexdigest()})
client_md5 = self.pro_recv()
if client_md5['md5num'] == md5.hexdigest():
log.logger.info(f"文件{fileinfo['filename']}上传并校验完成")
return True
else:
log.logger.warning(f"文件{fileinfo['filename']}上传完成但,校验失败")
return False
@checkquota
def download(self, fileinfo=dict(), userdic=dict()):
userdir = userdic['userdir']
listdir = list()
for file in os.listdir(userdir):
if file.startswith('_'):
pass
if file.startswith('.'):
pass
elif os.path.isfile(os.path.join(userdir, file)):
listdir.append(('f', file))
elif os.path.isdir(os.path.join(userdir, file)):
listdir.append(('d', file))
else:
pass
fileinfo['downfiles'] = listdir
self.pro_send(fileinfo)
fileinfo = self.pro_recv()
filepath = os.path.join(userdir, fileinfo['downfiles'])
filename = os.path.basename(filepath)
filesize = os.path.getsize(filepath)
fileinfo['filename'] = filename
fileinfo['filesize'] = filesize
self.pro_send(fileinfo)
send_stat = self.pro_recv()
break_point = False
if send_stat['continue'] == 'send':
break_point = True
received_size = send_stat['received']
filesize -= received_size
elif send_stat['continue'] == 'hasfile':
return True
with open(filepath, 'rb') as f:
if break_point: f.seek(received_size)
md5 = hashlib.md5()
while filesize > 2048:
content = f.read(2048)
self.request.send(content)
filesize -= 2048
md5.update(content)
else:
content = f.read()
self.request.send(content)
md5.update(content)
# check md5num
file_name = fileinfo['filename']
log.logger.info(f"文件{file_name}开始下载")
fileinfo = self.pro_recv()
if fileinfo['md5num'] == md5.hexdigest():
log.logger.info(f"文件{file_name}下载并校验完成")
self.pro_send({'md5check': True})
else:
log.logger.warning(f"文件{file_name}下载完成,但校验失败")
self.pro_send({'md5check': False})
def changedir(self, fileinfo=dict(), userdic=dict()):
fileinfo = fileinfo
userdir = userdic['userhome']
self.getfiles(userdir)
dic = {'dir':'/','files':self.getfiles(userdir)}
self.pro_send(dic)
nowdir = userdir
while True:
act = self.pro_recv()
if act['act'] == 'q':
log.logger.info(f"用户{userdic['username']}登出服务器")
break
elif act['act'] == 'ls':
dic['files'] = self.getfiles(nowdir)
self.pro_send(dic)
elif act['act'] == 'cd':
newdir = os.path.dirname(nowdir)
changedir = newdir.split(userdir)[1]
if changedir == '': changedir = '/'
nowdir = newdir
if nowdir == userdir:
dic = {'dir':'/','files':self.getfiles(userdir)}
self.pro_send(dic)
else:
dic = {'dir':changedir,'files': self.getfiles(nowdir)}
self.pro_send(dic)
elif act['act'] == 'mkdir':
os.makedirs(os.path.join(nowdir,act['dir_name']))
dic['files'] = self.getfiles(nowdir)
self.pro_send(dic)
log.logger.info(f"用户{userdic['username']}创建了{act['dir_name']}文件夹")
elif act['act'] == 'rm':
rmobj = act['rm_obj']
path = os.path.join(nowdir,rmobj)
if os.path.isdir(path):
try:
os.rmdir(path)
except OSError:
pass
else:
os.remove(path)
dic['files'] = self.getfiles(nowdir)
self.pro_send(dic)
if rmobj not in dic['files']:
log.logger.info(f"用户{userdic['username']}删除了{rmobj}")
elif act['act'] == 'upload':
userdic['userdir'] = nowdir
fileinfo=self.pro_recv()
self.upload(fileinfo,userdic)
elif act['act'] == 'download':
userdic['userdir'] = nowdir
fileinfo = self.pro_recv()
self.download(fileinfo,userdic)
else:
newdir = os.path.join(nowdir, act['act'][1])
nowdir = newdir
changedir = newdir.split(userdir)[1]
dic = {'dir': changedir, 'files': self.getfiles(nowdir)}
self.pro_send(dic)
Client
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import hmac
import json
import time
import socket
import struct
import hashlib
class FtpClient(object):
def __init__(self, addr, port):
self.addr = addr
self.port = port
self.sock = socket.socket()
self.sock.connect((self.addr, self.port))
self.sock.settimeout(10)
welcom = self.sock.recv(1024).decode('utf-8')
print(welcom if welcom != '' else '服务器连接失败。')
def pro_recv(self):
num = self.sock.recv(4)
num = struct.unpack('i', num)[0]
str_dic = self.sock.recv(num).decode('utf-8')
dic = json.loads(str_dic)
return dic
def pro_send(self, dic, pro=True):
bytes_dic = json.dumps(dic).encode('utf-8')
if pro:
len_bytes = struct.pack('i', len(bytes_dic))
self.sock.send(len_bytes)
self.sock.send(bytes_dic)
def makencrypt(secret_key):
def outter(func):
def inner(self, *args, **kwargs):
func_ret = func(self, *args, **kwargs)
rand = self.sock.recv(32)
h_mac = hmac.new(secret_key.encode('utf-8'), rand)
res = h_mac.digest()
self.sock.send(res)
return func_ret
return inner
return outter
@staticmethod
def bytes2human(num):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if num >= prefix[s]:
value = float(num) / prefix[s]
return '%.1f%s' % (value, s)
return '%sB' % num
def getquota(func):
def wrapper(self,*args,**kwargs):
ret = func(self,*args,**kwargs)
userquota = self.pro_recv()
human_used = self.bytes2human(userquota['used'])
human_total = self.bytes2human(userquota['total'])
if userquota['used'] > userquota['total']:
print(f'用户已经超过了配额,使用了{human_used},总共{human_total},请尽快充值账号')
else:
print(f'用户已经使用了{human_used},总共{human_total}')
return ret
return wrapper
@staticmethod
def progress(percent, width=50):
'''进度打印功能'''
if percent >= 100:
percent = 100
show_str = ('[%%-%ds]' % width) % (int(width *
percent / 100) * "#") # 字符串拼接的嵌套使用
print('\r%s %d%%' % (show_str, percent), end='')
@makencrypt(secret_key='alexsb')
def login(self):
print('开始登陆:')
user = input('用户名>>').strip()
pswd = input('密码>>>>').strip()
self.pro_send(
{'username': user, 'password': pswd, 'action': 'login', })
actstat = self.pro_recv()
print(actstat['message'])
return actstat['actflag']
@makencrypt(secret_key='alexsb')
def region(self):
print('开始注册:')
user = input('用户名>>').strip()
pswd = input('密码>>>>').strip()
self.pro_send(
{'username': user, 'password': pswd, 'action': 'region', })
actstat = self.pro_recv()
print(actstat['message'])
return actstat['actflag']
def logout(self):
self.sock.close()
exit()
@getquota
def upload(self, recv_size=0):
file_name = input('输入你要上传的文件>>>').strip()
if os.path.isfile(file_name):
filename = os.path.basename(file_name)
filesize = os.path.getsize(file_name)
fileinfo = {
'action': 'upload',
'filename': filename,
'filesize': filesize,
}
self.pro_send(fileinfo)
send_stat = self.pro_recv()
break_point = False
if send_stat['continue'] == 'send':
break_point = True
received_size = send_stat['received']
filesize -= received_size
recv_size += received_size
elif send_stat['continue'] == 'hasfile':
return True
with open(file_name, 'rb') as f:
if break_point: f.seek(received_size)
md5 = hashlib.md5()
while filesize > 2048:
content = f.read(2048)
self.sock.send(content)
md5.update(content)
filesize -= len(content)
recv_size += len(content)
recv_per = int(100 * recv_size / fileinfo['filesize'])
self.progress(recv_per, width=35)
else:
content = f.read()
md5.update(content)
self.sock.send(content)
recv_size += len(content)
recv_per = int(100 * recv_size / fileinfo['filesize'])
self.progress(recv_per, width=35)
# check md5num
server_md5 = self.pro_recv()['md5num']
time.sleep(1)
self.pro_send({'md5num':md5.hexdigest()})
if server_md5 == md5.hexdigest():
print(f" 文件{fileinfo['filename']}上传并校验完成。")
return True
else:
print(f" 文件{fileinfo['filename']}上传完成但,校验失败。")
return False
@getquota
def download(self, dir='download', recvsize=0):
if not os.path.exists(dir):
os.makedirs(dir)
fileinfo = {
'action': 'download',
}
self.pro_send(fileinfo)
dic = self.pro_recv()
print('服务器文件:')
for index, file in enumerate(dic['downfiles'], 1):
print(f'{index} {file[0]} {file[1]}')
while True:
filename = input('输入你想下载的文件名>>>').strip()
if filename.isdigit():
if int(filename) in range(len(dic['downfiles']) + 1):
filename = dic['downfiles'][int(filename) - 1]
if filename[0] == 'd':
print('无法下载文件夹,请重新选择文件。')
continue
break
else:
print('请输入正确的序号.')
continue
else:
filename = filename
break
dic['downfiles'] = filename[1]
self.pro_send(dic)
dic = self.pro_recv()
filepath = os.path.join(dir, dic['filename'])
filesize = dic['filesize']
if os.path.exists(filepath):
received_size = os.path.getsize(filepath)
if received_size == filesize:
self.pro_send({'continue':'hasfile'})
return True
else:
self.pro_send({'continue': 'send', 'received': received_size})
filesize -= received_size
recvsize += received_size
else:
self.pro_send({'continue': False})
with open(filepath, 'ab') as f:
md5 = hashlib.md5()
while filesize:
content = self.sock.recv(2048)
f.write(content)
filesize -= len(content)
recvsize += len(content)
recv_per = int(100 * recvsize / dic['filesize']) # 接收的比例
self.progress(recv_per, width=35) # 调用进度条函数,进度条的宽度默认设置为30
md5.update(content)
# check md5num
fileinfo['md5num'] = md5.hexdigest()
self.pro_send(fileinfo)
ret = self.pro_recv()['md5check']
if ret:
print(f" 文件{dic['filename']}下载完成并校验结束")
return True
else:
print(f" 文件{dic['filename']}校验出错")
return False
def changedir(self):
fileinfo = {
'action': 'changedir',
}
self.pro_send(fileinfo)
server_dir = self.pro_recv()
def showfile(server_dir):
print('服务器目录'.center(20, '='))
if server_dir['files'] == []:
print('\033[1;31m 当前目录下没有文件 \033[0m!')
else:
for index, files in enumerate(server_dir['files'], 1):
print(f'索引:{index} 类型:{files[0]} 名称:{files[1]}')
print(f"\033[7;22m当前目录为{server_dir['dir']}\033[0m")
print(''.center(20,'-'))
showfile(server_dir)
while True:
act = input('你要干哈?(cd/mkdir/ls/rm/upload/download/q)>').strip()
if act.isdigit():
if int(act) not in range(len(server_dir['files']) + 1):
print('\033[1;31m 请输入正确的序号 \033[0m!')
showfile(server_dir)
continue
elif server_dir['files'][int(act) - 1][0] == 'f':
print('\033[1;31m 文件不能切换 \033[0m!')
showfile(server_dir)
continue
else:
self.pro_send({'act': server_dir['files'][int(act) - 1]})
server_dir = self.pro_recv()
showfile(server_dir)
elif act == 'cd':
if server_dir['dir'] == '/':
print('\033[1;31m 当前已经为根目录无法进行切换 \033[0m!')
showfile(server_dir)
else:
dic = {'act':'cd'}
self.pro_send(dic)
server_dir = self.pro_recv()
showfile(server_dir)
elif act == 'mkdir':
dic = {'act':'mkdir'}
dir_name = input('你要创建的文件夹名称?').strip()
dic['dir_name'] = dir_name
if server_dir['files'] == []:
server_files = []
else:
server_files = [obj[1] for obj in server_dir['files']]
if dir_name in server_files:
print('\033[1;31m 文件夹创建失败,存在同名文件夹或文件 \033[0m!')
else:
self.pro_send(dic)
print(f'文件夹{dir_name}创建完成')
server_dir=self.pro_recv()
elif act == 'rm':
dic = {'act':'rm'}
rmobj = input('请输入你要删除的文件或空文件夹?').strip()
if server_dir['files'] == []:
server_files = []
print(f"{rmobj}不存在无法删除")
self.pro_send({'act':'q'})
return False
else:
server_files = [obj[1] for obj in server_dir['files']]
if rmobj not in server_files:
print(f"{rmobj}不存在无法删除")
self.pro_send({'act':'q'})
return False
dic['rm_obj'] = rmobj
self.pro_send(dic)
server_dir = self.pro_recv()
if server_dir['files'] == []:
server_files = []
else:
server_files = [obj[1] for obj in server_dir['files']]
if rmobj not in server_files:
print(f'\033[1;31m{rmobj}删除完成\033[0m!')
else:
print(f'\033[1;31m{rmobj}无法删除,不是空文件夹\033[0m!')
elif act == 'upload':
dic = {'act':'upload'}
self.pro_send(dic)
self.upload()
elif act == 'download':
dic = {'act':'download'}
self.pro_send(dic)
self.download()
elif act == 'ls':
dic = {'act':'ls'}
self.pro_send(dic)
time.sleep(1)
server_dir = self.pro_recv()
showfile(server_dir)
elif act == 'q':
self.pro_send({'act': 'q'})
break
else:
print('\033[1;31m请输入文件夹序号\033[0m!')
continue
def run(self):
print('1.登陆\n2.注册\n3.退出')
act = input('>>>').strip()
act_dic = {
'1': 'login', '登陆': 'login', 'login': 'login',
'2': 'region', '注册': 'region', 'region': 'region',
'3': 'logout', '退出': 'logout',
}
while True:
if act.isdigit():
if act in act_dic.keys():
if hasattr(self, act_dic[act]):
ret = getattr(self, act_dic[act])()
break
else:
continue
else:
continue
if ret:
try:
menudic = self.pro_recv()
except Exception as e:
self.sock.close()
exit('远程终端出错:%s' % e)
print(menudic['welcom'])
del menudic['welcom']
# for index, menu in enumerate(menudic, 1):
# print(index, menudic[menu])
# act = input('>>>').strip()
act = 'changedir'
act_dic = {
'1': 'upload', '上传': 'upload', 'U': 'upload', 'u': 'upload', 'upload': 'upload',
'2': 'download', '下载': 'download', 'D': 'download', 'd': 'download', 'download': 'download',
'3': 'changedir', '切换': 'changedir', '切换目录': 'changedir', 'c': 'changedir','changedir':'changedir',
}
if hasattr(self, act_dic[act]):
ret = getattr(self, act_dic[act])()
if ret:
print(act_dic[act]+'-done.')
self.sock.close()
if __name__ == '__main__':
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.join(BASE_DIR,'SocketServer'))
# print(sys.path)
from conf import settings
client = FtpClient('127.0.0.1', settings.PORT)
client.run()
脱产班第五次大作业-FTP服务器的更多相关文章
- Ubantu下FTP服务器资源进行控制
在FTP服务器的管理中无论对本地用户还是匿名用户,对于FTP服务器资源的使用都需要进行控控制, 避免由于负担过大造成FTP服务器运行异常, 可以添加以下配置项对FTP客户机使用FTP服务器资源进行控制 ...
- FTP服务器的搭建与配置
主要来源:http://www.cnblogs.com/helonghl/articles/5533857.html 1.安装FTP服务器: yum install vsftpd -y 2.启动FTP ...
- Python09作业思路及源码:高级FTP服务器开发(仅供参考)
高级FTP服务器开发 一,作业要求 高级FTP服务器开发 用户加密认证(完成) 多用户同时登陆(完成) 每个用户有不同家目录且只能访问自己的家目录(完成) 对用户进行磁盘配额,不同用户配额可不同(完成 ...
- java大作业博客--购物车
Java 大作业----使用MySQL的购物车 一.团队介绍 姓名 任务 李天明.康友煌 GUI设计及代码编写 谢晓淞 业务代码编写.MySQL服务器平台部署.git代码库 严威 类和包的结构关系设计 ...
- c++小学期大作业攻略(一)环境配置
UPDATE at 2019/07/20 20:21 更新了Qt连接mysql的方法,但是是自己仿照连VS的方法摸索出来的,简单测试了一下能work但是不保证后期不会出问题.如果你在尝试过程中出现了任 ...
- 【大数据应用技术】作业十二|Hadoop综合大作业
本次作业的要求来自:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3339 前言 本次作业是在<爬虫大作业>的基础上进行的 ...
- 大数据应用期末总评(hadoop综合大作业)
作业要求源于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/3363 一.将爬虫大作业产生的csv文件上传到HDFS (1)在/usr ...
- 大二小学期C#资产管理大作业小记
说明 这个程序是我大二夏季学期(俗称小学期)用Visual Studio + C#写的<资产管理>大作业.这个项目非常简单,就是用C#写出一个UI界面,并连接数据库进行增删改查.这是我第一 ...
- XML大作业
XML大作业 共两题,均于实验上机完成 第一题:在xml文档中使用DTD 第二题:掌握使用xsl显示xml文件的基本方法 第一题: 一.实验目的 (1)通过本实验,使学生能够了解并掌握XML DTD的 ...
随机推荐
- 洛谷P1605 迷宫 (DFS)
题目背景 迷宫 [问题描述] 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和 终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫 中移动有上下 ...
- 基于ROS和python,通过TCP通信协议,完成键盘无线控制移动机器人运动
一.所需工具包 1.ROS键盘包:teleop_twist_keyboard 2.TCP通讯包:socket $ cd ~/catkin_ws/src $ git clone https://gith ...
- 怎样从外网访问内网Sysbase数据库
外网访问内网Sysbase数据库 本地安装了Sysbase数据库,只能在局域网内访问,怎样从外网也能访问本地Sysbase数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Sys ...
- 关于mysql触发器和存储过程的理解
内容源自:一篇很棒的 MySQL 触发器学习教程 一.触发器概念 触发器(trigger):监视某种情况,并触发某种操作,它是提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊 ...
- Python while 循环
while循环的使用 count = 0 while True: print("conunt:",count) conunt = conunt +1 注:while:作为循环命令 ...
- Python selectors实现socket并发
selectors模块 此模块允许基于选择模块原语构建高级别和高效的I / O多路复用. 鼓励用户使用此模块,除非他们想要精确控制使用的os级别的原语. 注:selectors也是包装了select高 ...
- python传参是传值还是传引用
在此之前先来看看变量和对象的关系:Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象.而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的.例 ...
- iOS开发 -------- Block技术中的weak - strong
一 Block是什么? 我们使用^运算符来声明一个Block变量,而且在声明完一个Block变量后要像声明普通变量一样,后面要加; 声明Block变量 int (^block)(int) = NULL ...
- freeswitch 显示主叫名称和主叫号码
1.指定主叫号码 origination_caller_id_number 参数来指定显示的主叫号码 2.指定主叫名称 origination_caller_id_name 参数来指定显示的主叫名称 ...
- mysql 常用sql语句
权限 撤销权限revoke all on *.* from 'root'@'192.168.0.197' ; 撤销权限revoke all on *.* from 'xx_db' @'%'; 给指定用 ...