socketserver模块写的一个简单ftp程序
一坨需求...
- 用户加密认证
- 允许同时多用户登录
- 每个用户有自己的家目录 ,且只能访问自己的家目录
- 对用户进行磁盘配额,每个用户的可用空间不同
- 允许用户在ftp server上随意切换目录 (cd)
- 允许用户查看当前目录下文件 (ls)
- 允许上传和下载文件,保证文件一致性(get put)
- 文件传输过程中显示进度条
- 支持文件的断点续传
程序实现
1、README
- ### 作者信息
- 姓名: hexm
- email: xiaoming.unix@gmail.com
- ### 实现功能
- 用户加密认证
- 允许同时多用户登录
- 每个用户有自己的家目录 ,且只能访问自己的家目录
- 对用户进行磁盘配额,每个用户的可用空间不同
- 允许用户在ftp server上随意切换目录 (cd)
- 允许用户查看当前目录下文件 (ls)
- 允许上传和下载文件,保证文件一致性(get put)
- 文件传输过程中显示进度条
- 支持文件的断点续传
- ### 代码目录树
- ftpserver/
- ├── bin
- │ └── ftpserver.py
- ├── client
- │ └── ftp.py
- ├── db
- │ └── user.db
- ├── fstab
- ├── inittab
- ├── lib
- │ ├── common.py
- │ ├── __pycache__
- │ │ ├── common.cpython-35.pyc
- │ │ └── user_lib.cpython-35.pyc
- │ └── user_lib.py
- ├── src
- │ ├── ftpserver.py
- │ └── __pycache__
- │ └── ftpserver.cpython-35.pyc
- └── yum.conf
- ### 功能介绍
- 默认有一个用户,hexm,密码也是hexm,家目录为/tmp,限额1M,
- pwd 查看用户家目录
- cd 进入家目录其他目录,并且不能进入其他目录
- ls 列出家目录下内容
- put 上传文件 支持断点续传
- get 下载文件 支持断点续传
- bye 退出
- ### 操作步骤
- * 启动服务端
- python3 ftpserver/bin/ftpserver.py &
- * 启动客户端
- python3 ftpserver/client/ftp.py hexm@127.0.0.1 21
- >> 输入ftp密码:hexm
- 认证成功
- * ls 列出当前目录内容
- 列出当前目录内容
- ftp> ls
- etc/ fstab inittab pip-53mpg1m2-unpack/ pip-poq_f3ob-unpack/ yum.conf
- 列出某一目录内容
- ftp> ls etc
- fstab group inittab
- ftp> ls /etc
- fstab group inittab
- 显示多个文件详细信息
- ftp> ls -l yum.conf fstab
- -rw-r--r-- 1 root root 970 11月 20 11:46 /tmp/yum.conf
- -rw-r--r-- 1 root root 396 11月 19 19:44 /tmp/fstab
- * cd 进入其他目录
- 查看当前目录
- ftp> pwd
- /tmp
- 进入家目录中/etc目录
- ftp> cd /etc
- ftp> pwd
- /tmp/etc
- 返回上级目录
- ftp> cd ..
- ftp> pwd
- /tmp
- * 上传文件 支持断点续传
- 上传bash文件,因为家目录限额为1M,不能上传
- ftp> put /bin/bash
- 磁盘空间不足
- 上传文件不存在
- ftp> put /bin/ifstat
- 文件不存在
- 正常上传
- ftp> put /etc/fstab
- 开始上传[/etc/fstab]
- ====================================================================================================>100%
- * 下载文件,支持断点续传
- 发送并校验成功
- ftp> get yum.conf
- ====================================================================================================>100%
- * 退出
- ftp> bye
2、目录树
3、bin/ftpserver.py
- #!/usr/bin/env python
- # coding=utf-8
- import os
- import sys
- sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
- from src import ftpserver as src_server
- if __name__ == '__main__':
- src_server.run()
4、src/ftpserver.py
- #!/bin/python
- # coding=utf-8
- import socketserver
- import json
- import os
- import subprocess
- import re
- os.sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
- from lib.user_lib import User
- from lib.common import bytes_dumps, str_loads, bytes_encoding, str_encoding, getdirsize
- class FtpServer(socketserver.BaseRequestHandler):
- def handle(self):
- self.count = 0
- while True:
- recv_data = self.request.recv(1024)
- if len(recv_data) == 0: break
- task_data = str_loads(recv_data)
- task_action = task_data.get('action')
- # 根据客户端命令执行操作
- if hasattr(self, task_action):
- func = getattr(self, task_action)
- func(task_data)
- def auth(self, *args, **kwargs):
- """
- 验证用户合法性
- param auth_user: 用户名
- param auth_pwd: 密码
- param ret: 是否验证成功, 结果为True或False
- param self.home_path: 用户家目录
- param auth_pass_msg: 发送给客户端是否验证成功的信息
- """
- auth_user = args[0].get('user')
- auth_pwd = args[0].get('pwd')
- auth = User(auth_user, auth_pwd)
- ret = auth.login()
- if ret: # 认证成功获取用户家目录
- auth_pass_msg = {"status": "True"}
- self.home_path = auth.home_path()
- self.limit_home = auth.limit_home()
- else:
- auth_pass_msg = {"status": "False"}
- self.request.send(bytes_dumps(auth_pass_msg))
- def put(self, *args, **kwargs):
- file_size = args[0].get('file_size')
- file_name = args[0].get('file_name')
- md5sum = args[0].get('md5sum')
- abs_file = os.path.join(self.home_path, file_name)
- if os.path.isfile(abs_file): # 上传的文件存在
- if self._md5sum(abs_file) == md5sum: #上传的文件md5和本地的一致,不用重新上传
- status_msg = {"status": "same"}
- self.request.send(bytes_dumps(status_msg))
- else: # 不一致断点续传
- server_file_size = os.stat(abs_file).st_size
- status_msg = {"status": "add", "from_size": server_file_size}
- self.request.send(bytes_dumps(status_msg))
- with open(abs_file, 'ab') as f:
- recv_size = server_file_size
- while recv_size < file_size:
- data = self.request.recv(4096)
- f.write(data)
- recv_size += len(data)
- else: #上传文件不存在,开始写入
- # 上传的文件大小加上家目录大小 大于 限制的家目录大小,不允许上传
- if getdirsize(self.home_path) + int(file_size) > int(self.limit_home.strip('M')) * 1024 * 1024:
- status_msg = {"status": "less"}
- self.request.send(bytes_dumps(status_msg))
- else:
- status_msg = {"status": "True"}
- self.request.send(bytes_dumps(status_msg))
- with open(abs_file, 'wb') as f:
- recv_size = 0
- while recv_size < file_size:
- data = self.request.recv(4096)
- f.write(data)
- recv_size += len(data)
- request_client_md5 = {"option": "md5"}
- self.request.send(bytes_dumps(request_client_md5))
- md5sum = self._md5sum(abs_file)
- client_md5 = str_loads(self.request.recv(1024))
- if client_md5['md5'] == md5sum:
- check_msg = {"status": "True"}
- else:
- check_msg = {"status": "False"}
- self.request.send(bytes_dumps(check_msg))
- @staticmethod
- def _md5sum(filename):
- cmd = 'md5sum %s' % filename
- ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- md5sum = ret.stdout.read().split()[0]
- return str(md5sum, encoding='utf-8')
- def get(self, *args, **kwargs):
- file_name = args[0].get('file_name')
- abs_filename = os.path.join(self.home_path, file_name)
- if os.path.isfile(abs_filename):
- # 文件存在的话,向客户端发送此文件状态信息
- file_size = os.stat(abs_filename).st_size
- md5sum = self._md5sum(abs_filename)
- status_msg = {"status": "True", "file_size": file_size, "md5sum": md5sum}
- self.request.send(bytes_dumps(status_msg))
- # 客户端接收状态信息后,接收客户端回复信息
- client_ack_msg = self.request.recv(1024)
- client_ack_msg = str_loads(client_ack_msg)
- if client_ack_msg["status"] == 'True': # 客户端没有这个文件,完整下载
- with open(abs_filename, 'rb') as f:
- while True:
- filedata = f.read(4096)
- if not filedata: break
- self.request.send(filedata)
- client_request_md5 = str_loads(self.request.recv(1024))
- if client_request_md5['option'] == 'md5':
- md5sum = self._md5sum(abs_filename)
- md5_msg = {"md5": md5sum}
- self.request.send(bytes_dumps(md5_msg))
- elif client_ack_msg['status'] == 'same': # 客户端和服务端文件相同
- pass
- elif client_ack_msg['status'] == 'add': # 客户端有文件但不完整
- from_size = int(client_ack_msg['from_size'])
- with open(abs_filename, 'rb') as f:
- f.seek(from_size)
- while True:
- filedata = f.read(4096)
- if not filedata: break
- self.request.send(filedata)
- else:
- status_msg = {"status": "False"}
- self.request.send(bytes_dumps(status_msg))
- def cd(self, *args, **kwargs):
- if self.count == 0:
- self.tmp_home_path = self.home_path
- cd_dir = args[0].get('cd_dir')
- if cd_dir == '/':
- self.home_path = self.tmp_home_path
- status = {"status": "True"}
- elif cd_dir.startswith('/'):
- cd_dir = cd_dir[1:]
- if os.path.isdir(os.path.join(self.home_path, cd_dir)):
- self.home_path = os.path.join(self.home_path, cd_dir)
- status = {"status": "True"}
- else:
- status = {"status": "False"}
- elif cd_dir == '':
- self.home_path = self.tmp_home_path
- status = {"status": "True"}
- else:
- if os.path.isdir(os.path.join(self.home_path, cd_dir)):
- self.home_path = os.path.join(self.home_path, cd_dir)
- status = {"status": "True"}
- else:
- status = {"status": "False"}
- status = bytes_dumps(status)
- self.request.send(status)
- self.count += 1
- def pwd(self, *args, **kwargs):
- cmd = args[0].get('action')
- res = subprocess.Popen(cmd, shell=True, cwd=self.home_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- cmd_res = res.stdout.read()
- cmd_res_len = len(cmd_res)
- status_msg = {"status": "ready", "len_ret": cmd_res_len}
- self.request.send(bytes_dumps(status_msg))
- ack_msg = self.request.recv(1024)
- if str_loads(ack_msg).get('status') == 'yes':
- self.request.send(cmd_res)
- def ls(self, *args, **kwargs):
- cmd = args[0].get('action')
- args = args[0].get('args')
- if len(args) == 0:
- fnames = args = ''
- else:
- try:
- args, fnames = re.search('(.*-\w+ ?)(.*)', args).groups()
- except AttributeError as e:
- fnames = args
- args = ''
- cmd_res = ''
- if len(fnames.strip().split()) > 1:
- for i in fnames.strip().split():
- res = subprocess.Popen(cmd + ' ' + args + ' ' + self.home_path + '/' + i, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- cmd_res += str(res.stdout.read(), encoding='utf-8')
- cmd_res = bytes(cmd_res, encoding='utf-8')
- else:
- res = subprocess.Popen(cmd + ' ' + args + ' ' + self.home_path + '/' + fnames, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- cmd_res = res.stdout.read()
- if '-l' not in args:
- cmd_res = str(cmd_res, encoding='utf-8')
- tmp = []
- for res in cmd_res.strip().split():
- if os.path.isdir(os.path.join(self.home_path, res)):
- res = res + '/'
- tmp.append(res)
- cmd_res = ' '.join(tmp)
- cmd_res = bytes(cmd_res, encoding='utf-8')
- cmd_res_len = len(cmd_res)
- if not cmd_res:
- cmd_res = res.stderr.read()
- cmd_res_len = len(cmd_res)
- status_msg = {"status": "ready", "len_ret": cmd_res_len}
- self.request.send(bytes_dumps(status_msg))
- ack_msg = self.request.recv(1024)
- if str_loads(ack_msg).get('status') == 'yes':
- self.request.send(cmd_res)
- def _bytes_dumps(self, msg_data):
- return bytes(json.dumps(msg_data), encoding='utf-8')
- def _str_loads(self, msg_data):
- return json.loads(str(msg_data, encoding='utf-8'))
- def main():
- server = socketserver.ThreadingTCPServer(('0.0.0.0', 21), FtpServer)
- server.serve_forever()
- def run():
- main()
- if __name__ == '__main__':
- run()
5、client/ftp.py
- #!/usr/bin/env python
- # coding=utf-8
- import subprocess
- import socket
- import json
- import sys
- import os
- os.sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
- from lib.common import bytes_dumps, str_loads, bytes_encoding, str_encoding, view_bar
- class FtpClient(object):
- def __init__(self, fuser, fpwd, fip, fport=21):
- self.fuser = fuser
- self.fpwd = fpwd
- self.fip = fip
- self.fport = int(fport)
- self.s = socket.socket()
- def connect_socket(self):
- self.s.connect((self.fip, self.fport))
- def auth(self):
- auth_msg = {"action": "auth", "user": self.fuser, "pwd": self.fpwd}
- self.s.send(bytes(json.dumps(auth_msg), encoding='utf-8'))
- server_ack_msg = self.s.recv(1024)
- ack_data = json.loads(server_ack_msg.decode())
- if ack_data['status'] == 'True':
- return True
- else:
- return False
- def send_data(self):
- while True:
- send_data = input('ftp> ')
- if len(send_data) == 0:
- continue
- cmd_list = send_data.strip().split()
- if cmd_list == 0:
- continue
- cmd = cmd_list[0]
- if hasattr(self, cmd):
- func = getattr(self, cmd)
- func(cmd_list)
- else:
- print('--暂时不支持[%s]命令' % cmd)
- def put(self, *args):
- cmd = args[0][0]
- abs_filename = args[0][-1]
- if os.path.islink(abs_filename):
- print('符号链接不能上传')
- elif os.path.isfile(abs_filename):
- file_size = os.stat(abs_filename).st_size
- file_name = os.path.basename(abs_filename)
- md5sum = self._md5sum(abs_filename)
- msg_data = {"action": cmd, "file_name": file_name, "file_size":file_size, "md5sum": md5sum}
- self.s.send(bytes_dumps(msg_data))
- server_ack_msg = self.s.recv(1024)
- ack_data = str_loads(server_ack_msg)
- if ack_data['status'] == 'True':
- print('开始上传[%s]' % abs_filename)
- with open(abs_filename, 'rb') as f:
- send_size = 0
- while True:
- filedata = f.read(8192)
- send_size += len(filedata)
- if not filedata: break
- self.s.send(filedata)
- view_bar(send_size, file_size)
- sys.stdout.write('\n')
- server_request_md5 = str_loads(self.s.recv(1024))
- if server_request_md5['option'] == 'md5':
- md5sum = self._md5sum(abs_filename)
- md5_msg = {"md5": md5sum}
- self.s.send(bytes_dumps(md5_msg))
- md5_status = str_loads(self.s.recv(1024))
- if md5_status['status'] == 'True':
- print('发送并校验成功')
- else:
- print('上传文件失败')
- elif ack_data['status'] == 'add':
- with open(abs_filename, 'rb') as f:
- send_size = f.seek(ack_data['from_size'])
- print('开始上传[%s]' % abs_filename)
- while True:
- filedata = f.read(8192)
- send_size += len(filedata)
- if not filedata: break
- self.s.send(filedata)
- view_bar(send_size, file_size)
- sys.stdout.write('\n')
- print('增量上传成功')
- elif ack_data['status'] == 'same':
- print('开始上传[%s]' % abs_filename)
- print('====================================================================================================>100%')
- print('发送并校验成功')
- elif ack_data['status'] == 'less':
- print('磁盘空间不足')
- else:
- print('文件不存在')
- @staticmethod
- def _md5sum(filename):
- cmd = 'md5sum %s' % filename
- ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- md5sum = ret.stdout.read().split()[0]
- return str(md5sum, encoding='utf-8')
- def get(self, *args, **kwargs):
- cmd = args[0][0]
- abs_filename = args[0][-1]
- file_name = os.path.basename(abs_filename)
- msg_data = {"action": cmd, "file_name": file_name}
- self.s.send(bytes_dumps(msg_data))
- server_status_msg = self.s.recv(1024)
- status_msg = str_loads(server_status_msg)
- if status_msg['status'] == 'True': # 如果要下载的文件在服务端存在
- file_size = status_msg['file_size']
- if os.path.exists(file_name):
- if self._md5sum(file_name) == status_msg['md5sum']: # 本地文件和服务端相同,不用重新下载
- ack_msg = {"status": "same"}
- self.s.send(bytes_dumps(ack_msg))
- print('====================================================================================================>100%')
- print('下载并校验成功')
- else: # 文件存在,但校验和不同,说明没有下载完全
- local_file_size = os.stat(abs_filename).st_size
- ack_msg = {"status": "add", "from_size":local_file_size}
- self.s.send(bytes_dumps(ack_msg))
- with open(file_name, 'ab') as f:
- recv_size = local_file_size
- while recv_size < file_size:
- data = self.s.recv(4096)
- f.write(data)
- recv_size += len(data)
- view_bar(recv_size, file_size)
- sys.stdout.write('\n')
- else: # 本地没有这个文件,可以下载
- ack_msg = {"status": "True"}
- self.s.send(bytes_dumps(ack_msg))
- with open(file_name, 'wb') as f:
- recv_size = 0
- while recv_size < file_size:
- data = self.s.recv(4096)
- f.write(data)
- recv_size += len(data)
- view_bar(recv_size, file_size)
- sys.stdout.write('\n')
- request_server_md5 = {"option": "md5"}
- self.s.send(bytes_dumps(request_server_md5))
- md5sum = self._md5sum(file_name)
- server_md5 = str_loads(self.s.recv(1024))
- if server_md5['md5'] == md5sum:
- print('下载并校验成功')
- else:
- print('校验失败')
- else:
- print('服务端没有这个文件')
- def pwd(self, *args, **kwargs):
- if len(args[0]) != 1:
- print('错误的参数')
- else:
- cmd = args[0][0]
- msg_data = {"action": cmd}
- self.s.send(bytes_dumps(msg_data))
- server_ack_msg = self.s.recv(1024)
- ack_data = str_loads(server_ack_msg)
- if ack_data['status'] == 'ready':
- ack_msg = {"status": "yes"}
- self.s.send(bytes_dumps(ack_msg))
- cmd_recv_size = 0
- cmd_ret = b''
- while cmd_recv_size < ack_data['len_ret']:
- cmd_recv = self.s.recv(1024)
- cmd_ret += cmd_recv
- cmd_recv_size += len(cmd_recv)
- print(str(cmd_ret, encoding='utf-8'))
- def cd(self, *args, **kwargs):
- if len(args[0]) > 2:
- print('错误的参数')
- elif len(args[0]) == 1:
- cmd = args[0][0]
- cd_dir = ''
- else:
- cmd = args[0][0]
- cd_dir = args[0][1]
- msg_data = {"action": cmd, "cd_dir": cd_dir}
- self.s.send(bytes_dumps(msg_data))
- status = self.s.recv(1024)
- status = str_loads(status)
- if status['status'] == 'False':
- print('目录不存在')
- def bye(self, *args, **kwargs):
- sys.exit(0)
- def ls(self, *args):
- cmd = args[0][0]
- args = ' '.join(args[0][1:])
- msg_data = {"action": cmd, "args": args}
- self.s.send(bytes_dumps(msg_data))
- server_ack_msg = self.s.recv(1024)
- ack_data = str_loads(server_ack_msg)
- if ack_data['status'] == 'ready':
- ack_msg = {"status": "yes"}
- self.s.send(bytes_dumps(ack_msg))
- cmd_recv_size = 0
- cmd_ret = b''
- while cmd_recv_size < ack_data['len_ret']:
- cmd_recv = self.s.recv(1024)
- cmd_ret += cmd_recv
- cmd_recv_size += len(cmd_recv)
- print(str(cmd_ret, encoding='utf-8'))
- def help(self, *args, **kwargs):
- print('--支持如下命令: ls, cd, pwd, get, put, bye')
- def main():
- user, ip = sys.argv[1].strip().split('@')
- port = sys.argv[2]
- pwd = input('>> 输入ftp密码:').strip()
- ftp = FtpClient(user, pwd, ip, port)
- ftp.connect_socket()
- ret = ftp.auth()
- if ret:
- print('认证成功')
- ftp.send_data()
- else:
- print('认证失败')
- ftp.bye()
- ftp.send_data()
- def run():
- main()
- if __name__ == '__main__':
- run()
6、lib/common.py
- #!/usr/bin/env python
- # coding=utf-8
- import json
- import sys
- import os
- def bytes_dumps( msg_data):
- return bytes(json.dumps(msg_data), encoding='utf-8')
- def str_loads(msg_data):
- return json.loads(str(msg_data, encoding='utf-8'))
- def bytes_encoding(msg_data):
- return bytes(msg_data, encoding='utf-8')
- def str_encoding(msg_data):
- return str(msg_data, encoding='utf-8')
- def view_bar(num, total):
- rate = num / total
- rate_num = int(rate * 100)
- r = '\r%s>%d%%' % ("="*rate_num, rate_num, )
- sys.stdout.write(r)
- sys.stdout.flush()
- def getdirsize(dir):
- size = 0
- for root, dirs, files in os.walk(dir):
- size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
- return size
7、lib/user_lib.py
- #!/usr/bin/env python
- # coding=utf-8
- import hashlib
- import re
- import os
- BASEPATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
- USERDB = os.path.join(BASEPATH, 'db', 'user.db')
- class User(object):
- """
- 实现登陆注册功能
- """
- def __init__(self, user, password):
- self.username = user
- self.password = password
- @staticmethod
- def check(match, filedb):
- """
- 逐行匹配,查看是否用户名已经注册,不允许重复用户名
- """
- with open(filedb, 'r') as fuser:
- for line in fuser:
- if re.match(match + '\\b', line):
- return False
- else:
- continue
- return True
- @staticmethod
- def login_check(username, password, filedb):
- """
- 登陆验证
- """
- with open(filedb, 'r') as fuser:
- for line in fuser:
- user, pwd, _, _ = line.strip().split()
- if user == username and pwd == password:
- return True
- else:
- continue
- return False
- def home_path(self):
- """
- 返回用户家目录
- """
- with open(USERDB, 'r') as fpath:
- for line in fpath:
- if line.startswith(self.username):
- return line.strip().split()[-2]
- else:
- continue
- return False
- def limit_home(self):
- """
- 返回用户家目录限额
- """
- with open(USERDB, 'r') as flimit:
- for line in flimit:
- if line.startswith(self.username):
- return line.strip().split()[-1]
- else:
- continue
- return False
- def register(self):
- """
- 注册用户
- """
- passobj = hashlib.md5(bytes(self.password, encoding='utf-8'))
- passobj.update(bytes(self.password, encoding='utf-8'))
- secure_password = passobj.hexdigest()
- if self.check(self.username, USERDB):
- with open(USERDB, 'a') as fuser:
- fuser.write(self.username + ' ' + secure_password + '\n')
- return True
- else:
- print('用户名已存在')
- def login(self):
- """
- 用户登陆
- """
- passobj = hashlib.md5(bytes(self.password, encoding='utf-8'))
- passobj.update(bytes(self.password, encoding='utf-8'))
- secure_password = passobj.hexdigest()
- ret = self.login_check(self.username, secure_password, USERDB)
- if ret:
- return True
- else:
- return False
- def modify(self):
- """
- 修改密码
- """
- pass
8、db/user.db
- hexm 92df47e9074c048e0afe84ce0a5c407d /tmp 1M
- xm 92df47e9074c048e0afe84ce0a5c407d /tmp/sb 5M
socketserver模块写的一个简单ftp程序的更多相关文章
- 用python开发简单ftp程序
根据alex老师视频开发的简单ftp程序,只能实现简单的get功能 ftp客户端程序: #!/usr/bin/env python #_*_ coding:utf-8 _*_ import socke ...
- 写了一个简单的CGI Server
之前看过一些开源程序的源码,也略微知道些Apache的CGI处理程序架构,于是用了一周时间,用C写了一个简单的CGI Server,代码算上头文件,一共1200行左右,难度中等偏上,小伙伴可以仔细看看 ...
- 只是一个用EF写的一个简单的分页方法而已
只是一个用EF写的一个简单的分页方法而已 慢慢的写吧.比如,第一步,先把所有数据查询出来吧. //第一步. public IQueryable<UserInfo> LoadPagesFor ...
- 关于SIGSLOT的一个简单的程序
废话少说直接看代码即可,这只是一个简单的程序,可以帮我们简单地明白SIGSLOT是怎么回事.至于深入研究自己去百度吧. #include "sigslot.h" using nam ...
- 用Racket语言写了一个万花筒的程序
用Racket语言写了一个万花筒的程序 来源:https://blog.csdn.net/chinazhangyong/article/details/79362394 https://github. ...
- 自己写的一个简单PHP采集器
自己写的一个简单PHP采集器 <?php //**************************************************************** $url = &q ...
- 写了一个简单可用的IOC
根据<架构探险从零开始写javaweb框架>内容写的一个简单的 IOC 学习记录 只说明了主要的类,从上到下执行的流程,需要分清主次,无法每个类都说明,只是把整个主线流程说清楚,避免 ...
- 写了一个简单的 Mybatis
写了一个简单的 Mybatis,取名 SimpleMybatis . 具备增删改查的基本功能,后续还要添加剩下的基本数据类型和Java集合类型的处理. 脑图中有完整的源码和测试的地址 http://n ...
- 扩展Python模块系列(二)----一个简单的例子
本节使用一个简单的例子引出Python C/C++ API的详细使用方法.针对的是CPython的解释器. 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两 ...
随机推荐
- Android开发之Fragment
一.Fragment生命周期: 二.动态添加Fragment的三步: 1.获得Fragment的管理者FragmentManager FragmentManager fragmentManager = ...
- alarm
AlarmManager的使用机制有的称呼为全局定时器,有的称呼为闹钟.通过对它的使用,它的作用和Timer有点相似.都有两种相似的用法:(1)在指定时长后执行某项操作 (2)周期性的执行某项操作 在 ...
- 常用数据库高可用和分区解决方案(2) — MongoDB篇
MongoDB是当前比较流行的文档型数据库,其拥有易使用.易扩展.功能丰富.性能卓越等特性.MongoDB本身就拥有高可用及分区的解决方案,分别为副本集(Replica Set)和分片(shardin ...
- hibernate用setResultTransformer转换
当你用hibernate查出数据,但是类型不是原来的类型怎么办,新增的实体类还可以用,query.setResultTransformer(Transformers.aliasToBean(AA.cl ...
- 网络设计中需要考虑的时延latency差异
Jeff Dean提到不同数据访问方式latency差异 Numbers Everyone Should Know L1 cache reference 0.5 ns Branch mispredic ...
- 开发错误日记 12: Unsupported major.minor version 52.0
开发错误日记 12: Unsupported major.minor version 52.0 在编译时出现如下错误: java.lang.UnsupportedClassVersionError: ...
- java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 错误
你的ArrayList 是一个没有值的对象(不是null),也就是里面什么对象也没有存(即:arrayList.size()==0).但是,你有取它下标为0值的操作.所以,数组越界了!!比如array ...
- 512M内存编译php出错
make时错误如下 virtual memory exhausted: Cannot allocate memory make: *** [ext/fileinfo/libmagic/apprenti ...
- sublime text 如何设置”在浏览器浏览“的快捷键
sublime text编辑器极其强大 ,但在刚开始用的时候,每次在浏览器中查看非得右键鼠标----”open in browser“,着实觉得麻烦....百度之,上面的方法有很多,但是根据我自己的经 ...
- js json 对象相互转换
字符串转对象(strJSON代表json字符串) var obj = eval(strJSON); var obj = strJSON.parseJSON(); var obj = JSO ...