python day 18

2019/10/29

1. thinking in UML读书小感

这3天在看谭云杰的thinking in UML这本书,500多页的PDF扫描版,现在只看到279页,算是看完了一半,很多概念都是半懂不懂的,如在云山雾罩中一样。虽然看得不太明白,但是也有一些小感悟。

  1. 代码并不是全部,前期的需求分析,建模设计才是重点,这个就像行军打仗做好作战方略,备好粮草一样,后面的代码就是排兵步阵了。
  2. UML能看懂,不代表会画,会画的人必定是懂得RUP的人,这也解释了我这个初学者连一个小小的多用户登录FTP的程序的用例图都没画好的原因。
  3. 目标问题,我的目标是学会python,先掌握一门语言,而不是先上来就更高级的系统分析,有点好高骛远了。
  4. 不过,这本书看到,然后现在停下来,还是对我有不小的收获,对于整个软件开发的全貌有了不同的认识。同时也理解为什么很多公司不愿意招培训班或者自学的人了,因为如果只会写代码,像一些沟通用图如UML没有掌握,团队之间就不好沟通。

2. FTP作业重写

2.1 软件目录结构

按照老师的讲解,在命令行模式下输入python FTPServer.py start.

另一个命令行输入python FTPClient.py -s 127.0.0.1 -p 9999

2.2 FTPClient端脚本

2.2.1 bin目录下的FTPClient.py模块

import os
import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lib import client if __name__ == '__main__': client.Client(sys.argv)

2.2.2 config目录下的settings.py模块

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
USER_HOME = os.path.join(BASE_DIR, "db", "users")

2.2.3 lib目录下的client.py模块

import os
import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import hashlib
import json
import socket
from config import settings
import getpass
import time class Client(object):
def __init__(self, sys_argv):
self.USER_HOME = settings.USER_HOME
self.cwd = ""
self.args = sys_argv
self.HOST_IP = None
self.HOST_PORT = None
self.sock = None
self.logout_flag = False
self.response_code_dict = {
"100": "user successfully registerd",
"101": "username already existed,enter another username",
"200": "pass users authentication",
"201": "wrong username or password",
"202": "user does not exist",
"300": "ready to get file from server",
"301": "ready to send to server",
"302": "file doesn't exist on ftp server",
"303": "storage is full",
"601": "changed directory",
"602": "failed to find directory",
"2003": "already existed",
"2004": 'continue put',
"2005": "directory created"
}
self.argv_parse() def argv_parse(self):
if len(self.args) < 5:
self.help_msg()
sys.exit()
else:
mandatory_fields = ['-s', '-p']
for i in mandatory_fields:
if i not in self.args:
self.help_msg()
sys.exit()
try:
self.HOST_IP = self.args[self.args.index('-s') + 1]
self.HOST_PORT = int(self.args[self.args.index('-p') + 1])
self.handle()
except (IndexError, ValueError):
# 如果有索引错误,就打印帮助信息并退出程序
self.help_msg()
sys.exit("hhhh") def help_msg(self):
msg = """
input like below:\n
python FTPClient.py -s 127.0.0.1 -p 9999
"""
print(msg) def handle(self):
self.connect(self.HOST_IP, self.HOST_PORT)
while True:
username = input("username:>>>").strip()
password = getpass.getpass("password:>>>").strip()
md5 = hashlib.md5("lan".encode("utf-8"))
md5.update(password.encode("utf-8"))
password = md5.hexdigest()
user_pwd_dict = {"username": username, "password": password}
user_pwd = json.dumps(user_pwd_dict)
inp = input("请输入数字:1是登录,2是注册(q退出):>>>").strip()
if len(inp) < 1:
continue
if inp == '1':
if self.auth(user_pwd):
self.interactive()
elif inp == '2':
self.register(user_pwd)
elif inp.lower() == 'q':
break
else:
print("Invalid input")
self.sock.close() def connect(self, ip, port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((ip, port,)) def auth(self, user_pwd):
'''
验证用户名与密码
:param user_pwd:
:return:
'''
self.sock.sendall(("user_auth|%s" % user_pwd).encode("utf-8"))
data = json.loads(self.sock.recv(1024).decode("utf-8"))
if data["result"] == "200":
self.username = data["username"]
self.cwd = data["user_home"]
self.storage_limit = data["storage_limit"]
self.storage_used = data["storage_used"]
return True
elif data["result"] == "202":
print(self.response_code_dict["202"])
return False
else:
print(self.response_code_dict["201"])
return False def register(self, user_pwd):
'''
注册用户
:param user_pwd:
:return:
'''
self.sock.sendall(("user_register|%s" % user_pwd).encode("utf-8"))
msg = self.sock.recv(1024)
if msg == b"100":
print(self.response_code_dict["100"])
elif msg == b"101":
print(self.response_code_dict["101"]) def interactive(self):
while not self.logout_flag:
cmd = input("[%s %s]:" % (self.username, self.cwd)).strip()
if len(cmd) < 1: continue
cmd_str = "cmd_" + cmd.split()[0] if hasattr(self, cmd_str):
func = getattr(self, cmd_str)
func(cmd)
else:
print("Invalid command") def cmd_cd(self, cmd):
'''
切换路径
:param cmd:
:return:
'''
if len(cmd.split()) < 2: pass
input_path = cmd.split()[1].strip()
print("try_path:>>>", input_path)
dir_list = self.cwd.split(os.sep)
print("dir_list:>>>", dir_list) # ["lanxing",""]
if dir_list[-1] == "":
del dir_list[-1]
new_path = ""
if input_path == "..":
del dir_list[-1] # []
if len(dir_list) > 0:
new_path = os.sep.join(dir_list) + os.sep
else:
if input_path.startswith(self.cwd):
new_path = input_path
else:
new_path = os.path.join(self.cwd, input_path) + os.sep
print("new_path:>>>", new_path)
if not new_path.startswith(self.username):
pass
else:
data = {"cwd": new_path}
data_json = json.dumps(data)
self.sock.sendall(("cd|%s" % data_json).encode("utf-8"))
server_reply = self.sock.recv(1024)
auth_result = json.loads(server_reply.decode("utf-8"))
if auth_result["result"] == "601":
self.cwd = new_path
else:
print(self.response_code_dict["602"]) def cmd_ls(self, cmd):
msg = ""
if len(cmd.split()) == 1:
msg = json.dumps({"cwd": self.cwd}) elif len(cmd.split()) == 2:
path = cmd.split()[1]
dir_list = self.cwd.split()
new_path = ""
if dir_list[-1] == "":
del dir_list[-1]
if path == "..":
del dir_list[-1]
if len(dir_list) > 0:
new_path = os.sep.join(dir_list) + os.sep
else:
if path.startswith(self.user_def_cwd):
new_path = path
else:
new_path = self.user_def_cwd + path if not new_path.startswith(self.user_def_cwd):
pass
msg = json.dumps({"cwd": new_path})
self.sock.sendall(("ls|{0}".format(msg)).encode("utf-8"))
msg_size = int(self.sock.recv(1024).decode("utf-8"))
self.sock.sendall(b"300")
has_received = 0
auth_result = ""
while has_received < msg_size:
data = self.sock.recv(1024)
auth_result += data.decode("utf-8")
has_received += len(data)
auth_result = json.loads(auth_result)
if auth_result["result"]:
print(auth_result["result"])
else:
print(self.response_code_dict["302"]) def cmd_put(self, cmd):
data = {"file_size": None, "file_name": None, "dst_path": None} if len(cmd.split()) == 2:
src_file = cmd.split()[1]
dst_path = self.cwd
else:
src_file, dst_path = cmd.split()[1], cmd.split()[2]
if len(src_file.split(os.sep)) == 1:
src_file = os.path.join(settings.BASE_DIR, "bin", src_file)
if os.path.isfile(src_file):
file_size = os.stat(src_file).st_size
data["file_size"] = file_size
data["file_name"] = os.path.basename(src_file)
if dst_path.startswith("lanxing"):
data["dst_path"] = dst_path
else:
print("Wrong directory!")
data_json = json.dumps(data)
self.sock.sendall(("put|%s" % data_json).encode("utf-8"))
auth_result = json.loads(self.sock.recv(1024).decode("utf-8"))
print(auth_result)
has_sent = 0
with open(src_file, "rb") as f:
if auth_result["result"] == "2003":
print(self.response_code_dict["2003"], )
print("服务端同名文件大小:%s,本地文件大小:%s" % (auth_result["file_size"], file_size))
choice = input("Y:续传;N:覆盖 >>>").strip()
if choice.upper() == "Y":
f.seek(auth_result["file_size"])
self.sock.sendall(b"2004")
has_sent += auth_result["file_size"]
elif choice.upper() == "N":
self.sock.sendall(b"301")
elif auth_result["result"] == "302":
self.sock.sendall(b"301")
print(self.sock.recv(1024))
for line in f:
self.sock.sendall(line)
has_sent += len(line)
percent = has_sent / file_size * 100
sys.stdout.write("\r")
sys.stdout.write("%.2f%% |%s" % (percent, int(percent) * "*"))
sys.stdout.flush()
time.sleep(0.01) else:
print("file does not exist!") def cmd_get(self, cmd):
client_path = ""
data = {
"file_name": None,
"client_path": None,
"file_size": None,
"result":"300"
}
if len(cmd.split()) > 1:
file_path = cmd.split()[1]
file_name = os.path.basename(file_path)
if file_path.startswith(self.username):
client_path = file_path
else:
client_path = os.path.join(self.cwd, file_path)
if len(cmd.split()) == 2:
dst_path = os.path.join(settings.USER_HOME, self.cwd, file_name)
elif len(cmd.split()) == 3:
dst_path = cmd.split()[2]
if not dst_path.startswith(self.username):
dst_path = os.path.join(settings.USER_HOME, self.cwd, dst_path, file_name)
else:
dst_path = os.path.join(settings.USER_HOME, dst_path, file_name)
if os.path.exists(dst_path):
file_size = os.stat(dst_path).st_size
data["file_size"] = file_size
data["client_path"] = client_path
data_json = json.dumps(data)
self.sock.sendall(("get|%s" % data_json).encode("utf-8"))
server_reply = json.loads(self.sock.recv(1024).decode("utf-8"))
has_received = 0
if server_reply["result"]=="2003":
choice=input("目标文件已存在,续载Y或全部重新下载N:").strip()
if choice.upper() =="Y":
data["result"]="2004"
data_json = json.dumps(data).encode("utf-8")
self.sock.sendall(data_json)
has_received += file_size
try:
os.makedirs(os.path.dirname(dst_path))
except OSError:
pass
with open(dst_path,"ab") as f:
while has_received < server_reply["file_size"]:
ret = self.sock.recv(1024)
f.write(ret)
has_received += len(ret)
percent = has_received/server_reply["file_size"]*100
sys.stdout.write("\r")
sys.stdout.write("%.2f%%"%percent)
sys.stdout.flush()
elif choice.upper()=="N":
data["result"] = "300"
data_json = json.dumps(data).encode("utf-8")
self.sock.sendall(data_json)
try:
os.makedirs(os.path.dirname(dst_path))
except OSError:
pass
with open(dst_path, "wb") as f:
while has_received < server_reply["file_size"]:
ret = self.sock.recv(1024)
f.write(ret)
has_received += len(ret)
percent = has_received / server_reply["file_size"] * 100
sys.stdout.write("\r")
sys.stdout.write("%.2f%%" % percent)
sys.stdout.flush()
elif server_reply["result"]=="300":
data["result"] = "300"
data_json = json.dumps(data).encode("utf-8")
self.sock.sendall(data_json)
try:
os.makedirs(os.path.dirname(dst_path))
except OSError:
pass
with open(dst_path, "wb") as f:
while has_received < server_reply["file_size"]:
ret = self.sock.recv(1024)
f.write(ret)
has_received += len(ret)
percent = has_received / server_reply["file_size"] * 100
sys.stdout.write("\r")
sys.stdout.write("%.2f%%" % percent)
sys.stdout.flush() else:
print("Wrong instructions") def cmd_mkdir(self, cmd):
new_path = ""
if len(cmd.split()) <= 1:
pass
elif len(cmd.split()) == 2:
input_path = cmd.split()[1] if input_path.startswith(self.cwd):
new_path = input_path
else:
new_path = os.path.join(self.cwd, input_path)
print("new_path>>>", new_path)
msg = json.dumps({"cwd": new_path})
self.sock.sendall(("makedirs|{0}".format(msg)).encode("utf-8"))
auth_result = json.loads(self.sock.recv(1024).decode("utf-8"))
if auth_result["result"] == "2003":
print(self.response_code_dict["2003"])
else:
print(self.response_code_dict["2005"]) def cmd_exit(self, cmd):
self.logout_flag = True

2.3 FTPServer端脚本

2.3.1 bin目录下的FTPServer.py模块

import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lib import main if __name__ == '__main__':
main.ArgvHandler(sys.argv)

2.3.2 config目录下的settings.py模块

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
USER_HOME = os.path.join(BASE_DIR, "db", "users")
HOST_IP = "127.0.0.1"
HOST_PORT = 9999
USER_ACCOUNT_DIR = os.path.join(BASE_DIR, "db", "user_account")

2.3.3 lib目录下的main.py模块与ftp_server.py模块

main.py模块

import os, sys

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import socketserver
import ftp_server
from config import settings class ArgvHandler(object):
def __init__(self, sys_argv):
self.args = sys_argv
self.argv_handle() def argv_handle(self):
'''
处理命令行参数,看是否符合输入规范
:return:
'''
if len(self.args) < 2:
self.help_msg()
else:
first_argv = self.args[1] # first_argv = "start"
if hasattr(self, first_argv):
# 通过反射判断现有类的对象是否有start方法
func = getattr(self, first_argv)
# 有则通过反射拿到此方法,并运行此方法
func()
else:
self.help_msg() def help_msg(self):
msg = """
input like below:\n
python FTPServer start
""" def start(self):
"""
创建多线程socket对象,并让该对象一直运行
:return:
"""
try:
print("starting")
tcp_server = socketserver.ThreadingTCPServer((settings.HOST_IP, settings.HOST_PORT,),ftp_server.MyServer)
print("server started")
tcp_server.serve_forever()
except KeyboardInterrupt:
pass

ftp_server.py模块

import os
import sys sys.path.append(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import subprocess
import json
import socketserver
from config import settings class MyServer(socketserver.BaseRequestHandler):
response_code_dict = {
'100': 'user successfully registered',
'101': 'username already existed',
'200': 'Pass authentication!',
'201': 'Wrong username or password!',
'202': 'user does not exist',
'300': 'Ready to send file to client',
'301': 'Client ready to receive file',
'302': "File doesn't exist",
'2002': 'ACK(可以开始上传)',
'2003': 'already existed',
'2004': 'continue put',
"2005": "directory created"
} def handle(self):
while True:
# 死循环,一直接收客户端发过来的消息
data = self.request.recv(1024).decode()
if not data:
# 如果用户发过来的消息是空,则判定用户断开连接
break
data = data.split("|")
func_str = data[0] # 通过反射查看对象有无此属性
if hasattr(self, func_str):
func = getattr(self, func_str)
func(json.loads(data[1]))
else:
print("Invalid instructions") def user_auth(self, name_pwd):
username = name_pwd["username"]
password = name_pwd["password"]
auth_result = {
"username": username,
"password": password,
"storage_size": 0,
"storage_used": 0,
"result": "201",
"user_home": ""
}
with open(settings.USER_ACCOUNT_DIR, "r", encoding="utf-8") as f:
user_info = json.load(f)
if username in user_info.keys():
if password == user_info[username]["password"]:
self.login_user = username
path = os.path.join(settings.USER_HOME, username)
try:
os.makedirs(path)
except OSError:
pass
self.login_user_home = os.path.join(self.login_user) + os.sep
auth_result["user_home"] = self.login_user_home
auth_result["result"] = "200"
auth_result["storage_limit"] = user_info[username]["storage_limit"]
auth_result["storage_used"] = self.getdirsize(path)
else:
auth_result["result"] = "202"
data = json.dumps(auth_result).encode("utf-8")
self.request.sendall(data) def user_register(self, name_pwd):
with open(settings.USER_ACCOUNT_DIR, "r", encoding="utf-8") as f:
user_info = json.load(f)
if name_pwd["username"] in user_info:
self.request.sendall(b"101")
else:
name_pwd["storage_limit"] = 104857600
user_info[name_pwd["username"]] = name_pwd
self.request.sendall(b"100")
with open(settings.USER_ACCOUNT_DIR, "w", encoding="utf-8") as f:
json.dump(user_info, f) def cd(self, dir_str):
new_path = dir_str["cwd"]
# print(new_path)
server_path = settings.USER_HOME + os.sep + new_path
# print(server_path)
auth_result = {"result": "602"}
if os.path.exists(server_path):
auth_result["result"] = "601"
auth_result_json = json.dumps(auth_result)
self.request.sendall(auth_result_json.encode("utf-8")) def ls(self, ins):
dir_str = ins["cwd"]
server_path = os.sep.join([settings.USER_HOME, dir_str])
auth_result = {"result": None}
if os.path.exists(server_path):
path = os.path.join(settings.USER_HOME, server_path)
if sys.platform == "win32":
command = "dir" + " " + path
else:
command = "ls" + " " + path
auth_result["result"] = subprocess.getoutput(command)
msg_size=len(json.dumps(auth_result).encode("utf-8"))
self.request.sendall(str(msg_size).encode("utf-8"))
self.request.recv(1024)
self.request.sendall(json.dumps(auth_result).encode("utf-8")) def put(self, data):
file_size = data["file_size"]
file_name = data["file_name"]
dst_path = data["dst_path"]
file_path = os.path.join(settings.USER_HOME, dst_path, file_name)
# print(file_path)
if os.path.exists(file_path):
file_size2 = os.stat(file_path).st_size
if file_size2 <=file_size:
data["file_size"] = file_size2
data["result"] = "2003" else:
data["result"] = "302"
print(data)
data_json = json.dumps(data)
self.request.sendall(data_json.encode("utf-8"))
client_msg = self.request.recv(1024).decode("utf-8")
has_received = 0
if client_msg == "2004":
has_received += file_size2
with open(file_path, "ab") as f:
self.request.sendall(b"2002")
while has_received < file_size:
data1 = self.request.recv(1024)
f.write(data1)
has_received += len(data1) elif client_msg =="301":
try:
os.makedirs(os.path.dirname(file_path))
except OSError:
pass
with open(file_path, "wb") as f:
self.request.sendall(b"2002")
while has_received < file_size:
data1 = self.request.recv(1024)
f.write(data1)
has_received += len(data1) def get(self, data):
client_path = data["client_path"]
print("client_path>>>",client_path)
file_size = data["file_size"]
data["result"]="2003"
server_path = os.path.join(settings.USER_HOME,client_path)
if os.path.isfile(server_path):
file_size2 = os.stat(server_path).st_size
if not file_size:
data["result"]="300"
data["file_size"] = file_size2
data_json = json.dumps(data).encode("utf-8")
self.request.sendall(data_json)
client_reply = json.loads(self.request.recv(1024).decode("utf-8"))
with open(server_path,"rb") as f :
if client_reply["result"] == "2004":
f.seek(file_size)
for line in f:
self.request.sendall(line) def getdirsize(self, path):
file_size = 0
for root, dirs, files in os.walk(path):
file_size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
return file_size def makedirs(self, data):
path = data["cwd"]
server_path = os.path.join(settings.USER_HOME, path)
print(server_path)
auth_result = {"result": None}
if os.path.exists(server_path):
auth_result["result"] = "2003"
else:
os.makedirs(server_path)
auth_result["result"] = "2005"
msg = json.dumps(auth_result).encode("utf-8")
self.request.sendall(msg)

python day 18: thinking in UML与FTP作业重写的更多相关文章

  1. 十八. Python基础(18)常用模块

    十八. Python基础(18)常用模块 1 ● 常用模块及其用途 collections模块: 一些扩展的数据类型→Counter, deque, defaultdict, namedtuple, ...

  2. python之ftp作业【还未完成】

    作业要求 0.实现用户登陆 1.实现上传和下载 3.每个用户都有自己的家目录,且只可以访问自己的家目录 4.对用户进行磁盘配额,每个用户的空间不同,超过配额不允许下载和上传 5.允许用户在指定的家目录 ...

  3. Python学习笔记——基础篇【第七周】———FTP作业(面向对象编程进阶 & Socket编程基础)

    FTP作业 本节内容: 面向对象高级语法部分 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 参考:http://www.cnblogs.com/wupeiqi/ ...

  4. python day33 ,socketserver多线程传输,ftp作业

    一.一个服务端连多个客户端的方法 1.服务端 import socketserver class MyServer(socketserver.BaseRequestHandler): def hand ...

  5. python全栈开发day29-网络编程之socket常见方法,socketserver模块,ftp作业

    一.昨日内容回顾 1.arp协议含义 2.子网,子网掩码 3.两台电脑在网络中怎么通信的? 4.tcp和udp socket编码 5.tcp和udp协议的区别 6.tcp三次握手和四次挥手,syn洪攻 ...

  6. Python window console 控制台 实现最后一行输出 print 重写

    Python window console 控制台 实现最后一行输出 print 重写 # -*- coding: utf-8-*- from __future__ import print_func ...

  7. python 开发一个支持多用户在线的FTP

    ### 作者介绍:* author:lzl### 博客地址:* http://www.cnblogs.com/lianzhilei/p/5813986.html### 功能实现 作业:开发一个支持多用 ...

  8. python基础——18(面向对象2+异常处理)

    一.组合 自定义类的对象作为另一个类的属性. class Teacher: def __init__(self,name,age): self.name = name self.age = age t ...

  9. Python实现支持并发、断点续传的FTP

    参考网上一个FTP程序,重写了一遍,并稍加扩展 一.要求 1. 支持多用户同时登录 2. 可以注册用户,密码使用md5加密 3. 可以登录已注册用户 4.  支持cd切换目录,ls查看目录子文件 5. ...

随机推荐

  1. How to Use Convolutional Neural Networks for Time Series Classification

    How to Use Convolutional Neural Networks for Time Series Classification 2019-10-08 12:09:35 This blo ...

  2. JS项目快速压缩(windows平台)

    问题 当下JS项目都有node_modules,从而项目文件多,容量大. 如何快速压缩一个JS项目? 方法 首先对JS项目安装生产环境的依赖npm install --production. 这时不要 ...

  3. redis 链接

    一.redis启动: 本地启动:redis-cli 远程启动:redis-cli -h host -p port -a password 例如:redis-cli -h r-2mlmkmxu7.red ...

  4. java导出pdf功能记录

    这几天已在做处理导出pdf文件的功能,摸索了几天总算可以了.记录下这几天遇到的问题. 1.网上基本都是基于Itext5和Itext7来处理的.我最终是在Itext5上成功了,itext7应该是模板出问 ...

  5. GPRS 智能门禁控制器

    本模块居于GPRS 2G网络,信号覆盖广,而且好. 主要用于微信门禁等,提供用户服务端搭建及相关接口. 您可以向门禁发送开门信号,并提醒开门成功的信号反馈. 同时支持发送开门ID号,并反馈成功ID号

  6. Kubernetes之使用kubeadm部署

    参考:https://www.cnblogs.com/caoxb/p/11243472.html 部署虚拟机规划 192.168.1.11 k8s-master 192.168.1.12 k8s-no ...

  7. Java中常量以及常量池

    1.举例说明 变量 常量 字面量 int a=10; float b=1.234f; String c="abc"; final long d=10L; a,b,c为变量,d为常量 ...

  8. springboot 控制台程序读取配置文件(原创)

    首先新建一个springboot项目,此处省略. 1.新建一个application.properties person.name=kevin person.age=6 person.sex=male ...

  9. Linux下使用matlab在后台默默的运行.m文件(无界面形式)

    Linux下使用matlab在后台默默的运行.m文件(无界面形式)本主在Ubuntu18.04LTS上已经安装了matlab直接运行Matlab$ matlab会启动 matlab,出现启动界面但想要 ...

  10. Centos7 yum方式安装MySQL

    1.下载安装源 wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm 2.yum方式安装 yu ...