教程
信息差
资源
软件工具
技术笔记
AIGC
视频
Search
1
使用AI实现高精度钢琴曲转谱Piano Transcription简明使用教程
37,794 阅读
2
使用ESP8266Wifi模块制作Wifi杀手
37,467 阅读
3
unravel 让图片唱歌详细教程 Real Time Image Animation 项目
27,386 阅读
4
佳能相机刷Magic Lantern魔灯固件
23,503 阅读
5
战地3 正版账号免费分享!
16,213 阅读
教程
信息差
资源
软件工具
技术笔记
AIGC
视频
Search
标签搜索
python
前端
环境搭建
空镜素材
Ubuntu
markdown
神器
黑苹果
编码
技巧
Git
数据库
开发
下载工具
Youtube
CDN
PDF
OST
电影原声带
音乐
易小灯塔
累计撰写
176
篇文章
累计收到
44
条评论
首页
栏目
教程
信息差
资源
软件工具
技术笔记
AIGC
视频
页面
搜索到
175
篇与
的结果
2017-10-13
Django框架踩过的坑 02
Django框架踩过的坑 021:TypeError: unhashable type: ‘dict’遇到这种问题是因为使用的字典的键是不可哈希的,能够哈希的是一些不可变类型的int,string之类的,而list,dict就不行了。2:append方法和extend等列表方法是没有返回值的,是没有返回值的值,如果直接返回结果只能None,在实际操作过程中吃了两次亏了。3:django很久没有用了,结果用的时候手忙脚乱不知所措,究其本质还是自己了解地不够透彻,既然老是在同一个地方查资料,还不如自己写个方便自己查看:首先是url的配置,在主urls文件中导入需要的包from django.conf.urls import url,include备用,接着处理app中的urls文件(这个是自己手动构建的),同样导入一些调料包:from django.conf.urls import url,当然还有就是需要处理的视图函数所在的路径,然后开始下锅:在app的urls.py中写urlpatterns=[url(''),function_name,],写好之后返回管家婆主urls文件中,告诉它urlpatterns=[url(r'^app_name'),include('one_app.urls'),]这就相当于告诉主人:url以我的app_name开始的路由就指向我的私人路由。4:写接口的时候在路由后面加一个斜杠?然后写上在视图函数中接收参数的参数名也就是这个里面的param_name(而且需要加等号以及后面的内容,无论是不是字符串都不能加引号,很多情况下都是json格式的说)request.GET.get('param_name')。
2017年10月13日
751 阅读
0 评论
0 点赞
2017-10-12
Django框架踩过的坑 01
在admin.py中写fieldsetsfieldsets = [ ('base', {'field': ['btitle']}), ('super', {'field': ['bpub_date']}), ] 元组间没有加,,导致 无法启动Django项目,报错如下:Traceback (most recent call last): File "manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line utility.execute() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 312, in execute django.setup() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/__init__.py", line 18, in setup apps.populate(settings.INSTALLED_APPS) File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/apps/registry.py", line 115, in populate app_config.ready() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/contrib/admin/apps.py", line 22, in ready self.module.autodiscover() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/contrib/admin/__init__.py", line 24, in autodiscover autodiscover_modules('admin', register_to=site) File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/utils/module_loading.py", line 74, in autodiscover_modules import_module('%s.%s' % (app_config.name, module_to_search)) File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module __import__(name) File "/home/python/Desktop/django/test1/booktest/admin.py", line 7, in <module> class BookInfoAdmin(admin.ModelAdmin): File "/home/python/Desktop/django/test1/booktest/admin.py", line 14, in BookInfoAdmin ('super', {'field': ['bpub_date']}) TypeError: 'tuple' object is not callable
2017年10月12日
1,129 阅读
0 评论
0 点赞
2017-10-07
八大排序算法与比较
直接插入排序 时间复杂度:O(n²) 空间复杂度:O(1) 稳定性:稳定 def insert_sort(array): for i in range(len(array)): for j in range(i): if array[i] < array[j]: array.insert(j, array.pop(i)) break return array 希尔排序 时间复杂度:O(n) 空间复杂度:O(n√n) 稳定性:不稳定 def shell_sort(array): gap = len(array) while gap > 1: gap = gap // 2 for i in range(gap, len(array)): for j in range(i % gap, i, gap): if array[i] < array[j]: array[i], array[j] = array[j], array[i] return array 简单选择排序 时间复杂度:O(n²) 空间复杂度:O(1) 稳定性:不稳定 def select_sort(array): for i in range(len(array)): x = i # min index for j in range(i, len(array)): if array[j] < array[x]: x = j array[i], array[x] = array[x], array[i] return array 堆排序 时间复杂度:O(nlog₂n) 空间复杂度:O(1) 稳定性:不稳定 def heap_sort(array): def heap_adjust(parent): child = 2 * parent + 1 # left child while child < len(heap): if child + 1 < len(heap): if heap[child + 1] > heap[child]: child += 1 # right child if heap[parent] >= heap[child]: break heap[parent], heap[child] = \ heap[child], heap[parent] parent, child = child, 2 * child + 1 heap, array = array.copy(), [] for i in range(len(heap) // 2, -1, -1): heap_adjust(i) while len(heap) != 0: heap[0], heap[-1] = heap[-1], heap[0] array.insert(0, heap.pop()) heap_adjust(0) return array 冒泡排序 时间复杂度:O(n²) 空间复杂度:O(1) 稳定性:稳定 def bubble_sort(array): for i in range(len(array)): for j in range(i, len(array)): if array[i] > array[j]: array[i], array[j] = array[j], array[i] return array 快速排序 时间复杂度:O(nlog₂n) 空间复杂度:O(nlog₂n) 稳定性:不稳定 def quick_sort(array): def recursive(begin, end): if begin > end: return l, r = begin, end pivot = array[l] while l < r: while l < r and array[r] > pivot: r -= 1 while l < r and array[l] <= pivot: l += 1 array[l], array[r] = array[r], array[l] array[l], array[begin] = pivot, array[l] recursive(begin, l - 1) recursive(r + 1, end) recursive(0, len(array) - 1) return array 归并排序 时间复杂度:O(nlog₂n) 空间复杂度:O(1) 稳定性:稳定 def merge_sort(array): def merge_arr(arr_l, arr_r): array = [] while len(arr_l) and len(arr_r): if arr_l[0] <= arr_r[0]: array.append(arr_l.pop(0)) elif arr_l[0] > arr_r[0]: array.append(arr_r.pop(0)) if len(arr_l) != 0: array += arr_l elif len(arr_r) != 0: array += arr_r return array def recursive(array): if len(array) == 1: return array mid = len(array) // 2 arr_l = recursive(array[:mid]) arr_r = recursive(array[mid:]) return merge_arr(arr_l, arr_r) return recursive(array) 基数排序 时间复杂度:O(d(r+n)) 空间复杂度:O(rd+n) 稳定性:稳定 def radix_sort(array): bucket, digit = [[]], 0 while len(bucket[0]) != len(array): bucket = [[], [], [], [], [], [], [], [], [], []] for i in range(len(array)): num = (array[i] // 10 ** digit) % 10 bucket[num].append(array[i]) array.clear() for i in range(len(bucket)): array += bucket[i] digit += 1 return array 速度比较from random import random from json import dumps, loads # 生成随机数文件 def dump_random_array(file='numbers.json', size=10 ** 4): fo = open(file, 'w', 1024) numlst = list() for i in range(size): numlst.append(int(random() * 10 ** 10)) fo.write(dumps(numlst)) fo.close() # 加载随机数列表 def load_random_array(file='numbers.json'): fo = open(file, 'r', 1024) try: numlst = fo.read() finally: fo.close() return loads(numlst) 如果数据量特别大,采用分治算法的快速排序和归并排序,可能会出现递归层次超出限制的错误。解决办法:导入 sys 模块(import sys),设置最大递归次数(sys.setrecursionlimit(10 ** 8))。@exectime def bubble_sort(array): for i in range(len(array)): for j in range(i, len(array)): if array[i] > array[j]: array[i], array[j] = array[j], array[i] return array array = load_random_array() print(bubble_sort(array) == sorted(array)) ↑ 示例:测试直接插入排序算法的运行时间,@exectime 为执行时间装饰器。 算法执行时间算法速度比较
2017年10月07日
2,342 阅读
0 评论
0 点赞
2017-08-20
Python的虚拟环境virtualenv
Python的虚拟环境可以使一个Python程序拥有独立的库library和解释器interpreter,而不用与其他Python程序共享统一个library和interpreter。虚拟环境的好处是避免了不同Python程序间的互相影响(共同使用global library 和 interpreter),例如程序A需要某个库的1.0版本,而程序B需要同样这个库的2.0版本,如果程序B执行则A就不能执行了。安装virtualenv:$ sudo pip install virtualenv $ sudo pip install virtualenvwrapper 创建目录用来存放虚拟环境mkdir ~/.virtualenvs 打开~/.bashrc文件,并添加如下:export WORKON_HOME=$HOME/.virtualenvs source ~/.local/bin/virtualenvwrapper.sh 更新配置文件$ source ~/.bashrc 创建虚拟环境$ virtualenv -p python3 虚拟环境名称 查看虚拟环境的命令 :$ workon 两次tab键 使用虚拟环境的命令$ workon 虚拟环境名称 退出虚拟环境$$ deactivate 删除虚拟环境$ rmvirtualenv 虚拟环境名 或者 $ rm -r 虚拟环境名
2017年08月20日
1,294 阅读
0 评论
0 点赞
2017-08-19
Git详细教程
Git能解决什么问题?答曰:版本控制。经常写文档的同学应该比较清楚,对某个文档修改了一点以后,又不想直接覆盖,这样的话,后面发现写错了,就恢复不回来。所以就复制出很多文件名不一样,但是内容差不多的文件。这样也不是不可以,但是问题在于,Copy了很多份,太消耗空间。同时查找恢复也并不方便。所以我们希望有这样一种软件,它可以 自动记录文件的改动 可以团队协作编辑 比如这样 版本 文件名 用户 说明 日期 1 service.doc 张三 删除了软件服务条款5 7/12 10:38 2 service.doc 张三 增加了License人数限制 7/12 18:09 3 service.doc 李四 财务部门调整了合同金额 7/13 9:51 4 service.doc 张三 延长了免费升级周期 7/14 15:17 其实仅仅是对Word文档进行版本控制,我觉得有道云协作就可以了,但是它需要使用外网,而且不是利用Windows自带的目录,感觉还是不太方便。分布式版本控制平台其实版本控制器还有很多,比如CVS和SVN,但是它们都是集中式的控制系统。所谓集中式,自然有个Master级别的角色,它可以保存所有的版本库。大家需要先从版本库里面获得最新的版本,修改以后再上传。这样Master自然就有了所有分支最新的版本了。缺点是必须联网,是不是很类似与上面说到的有道云笔记啊!那分布式版本控制系统有啥区别呢?分布式系统没有Master这个角色,所有的终端一视同仁,每个人都有一个完整的版本库。那怎么协作呢?只需要互相通信,互相推送就可以了。分布式系统的优点在于安全,一个人的电脑坏了,还有其他人的电脑作为备份嘛。当然在实际应用的时候,一般不会有两个人互相推送,还是会引入一个中央服务器,但是它就类似于一个交换机,只是用来交互数据,没有它大家也可以在本地干活。而且Git还有强大的分支管理功能,还是免费的。现在最快、最简单也最流行的就是Git了。#安装Git Linux上安装 $ sudo apt-get install git Windows上安装 参考【使用教程】CMDer,Window下CMD的替代者 安装Cmder即可,里面就自带了git功能。 配置首先要进行全局设置:$ git config --global user.name "Your Name" $ git config --global user.email "email@example.com" 提交版本创建版本库什么是版本库?就是仓库,respository,可以理解为一个目录,里面的所有文件可以被Git管理起来,里面的文件修改、删除都跟跟踪到。下面来创建版本库,在需要创建版本库的地方里面打开cmder,输入$ git init 当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的某些文件不提交有些时候需要把某些文件放到目录中,但是又不能提交它。可以在Git工作区建立一个.gitignore文件,里面填充要忽略的文件名我们可以通过.gitignore网站查看各种配置文件,组合一下即可用。一般需要忽略那些文件呢? 操作系统自动生成的文件或者编译生成的文件。 忽略敏感信息,比如密码 比如Python里面, # Windows: Thumbs.db ehthumbs.db Desktop.ini # Python: *.py[cod] *.so *.egg *.egg-info dist build # My configurations: db.ini deploy_key_rsa 有的时候,想添加一个文件到Git,发现添加不了,多半是被忽略了。可以使用$ git add -f <文件> 也可以看是哪条规则限制了这个文件的上传$ git check-ignore -v <文件> 将文件提交到版本库里面所有的版本控制系统都只能跟踪文本文件的改动,比如TXT文件、代码等。对于图片、视频、Word等都是二进制文件,虽然仍然可以由版本控制系统管理,但是没有办法对比文件系统的变化。也就是说通过Cmder无法对比两个版本之间的差别。所以使用Git主要还是针对于代码文件、TXT文件等进行版本控制,需要注意的是使用windows进行编码的时候,建议使用Notepad++将默认编码设置为UTF-8 without BOM添加文件到Git仓库,分两步: 第一步,使用命令git add .,注意,可反复多次使用,添加多个文件; 要随时掌握工作区的状态,使用git status命令。 如果git status告诉你有文件被修改过,用git diff readme.txt可以查看修改内容。 git diff HEAD -- readme.txt可以看查看工作区和版本库里面最新版本的区别 第二步,使用命令git commit -m "备注"进行正式提交。 实际上每次执行git commit -m就保存了一次快照,类似于打游戏的时候存一次档。如果我们想回退的话,可以通过快照来进行恢复rollback 可以使用git log --pretty=oneline命令显示从最近到最远的提交日志,以时间轴的形式显示日志提交。 版本回退上面一章我们讲了,可以使用git commit进行提交,然后使用git log --pretty=oneline查看有提交的版本。$ git log --pretty=oneline 3628164fb26d48395383f8f31179f24e0882e1e0 append GPL ea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributed cb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file 其中类似3628164...882e1e0的是commit id(版本号),为了保证多人提交的环境下,commit id不同,所以Git使用了SHA1计算出来的一个非常大的数字,用十六进制表示,这样就可以避免冲突了。Git就会把每个版本自动串成一条时间线如何进行版本回退首先,Git必须知道当前版本是哪个版本, 用HEAD表示当前版本, 上一个版本就是HEAD^ 上上一个版本就是HEAD^^, 当然往上100个版本写成HEAD~100 然后使用git reset开始回退git reset --hard HEAD^ 如果发现回退错了,所以想回到最开始的版本。 如果命令行窗口还没有被关掉,可以顺着往上找直到找到那个版本的ID是3628164...,于是就可以指定回到未来的某个版本: $ git reset --hard 3628164 如果命令行窗口关闭了,可以使用git reflog来查看执行commit命令时候的commit id。然后使用git reset 总结一下: 现在总结一下: HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。 几个概念首先解释几个名词: 工作区:指的是建立了git的目录,也就是平时我们进行代码编辑的地方 版本库:工作区有一个隐藏目录.git,是Git的版本库。 版本库里面有暂存区(Stage)、分支(Master)以及指向分支的指针HEAD 其中Git区别于其他的版本控制系统的一个不同之处就是有了暂存区 git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。理解了这个,我们来可如下的过程第一次修改 -> git add -> 第二次修改 -> git commit可以发现第二次修改以后并没有git add,也就是第二次修改的内容没有放到暂存区,所以git commit不会把第第二次的修改提交了。这就是Git比其他版本控制系统优秀的地方,因为Git跟踪管理的是修改,而不是文件没有提交到分支之前的撤销之前我们说到了如果已经git commit到分支以后,要进行版本回退应该怎么做。但是如果我们只是添加到了暂存区,甚至还没提交到暂存区,此时应该如何撤销呢? 只是修改,没有add到暂存区: $ git checkout -- file 使用这个命令还可以把误删的文件恢复回来已经add到暂存区,但是没有commit 首先使用git reset HEAD file回退到工作区。 然后使用git checkout -- file把工作区的修改撤销了。 总之,记住一点:git checkout -- 文件命令,撤销的是工作中文件的修改,而git reset HEAD -- 文件命令,撤销的是暂存区中文件的修改。撤销本地所有修改$ git checkout . #本地所有修改的。没有的提交的,都返回到原来的状态 $ git stash #把所有没有提交的修改暂存到stash里面。可用git stash pop回复。 $ git reset --hard HEAD #返回到某个节点,不保留修改。 分支分支有什么用?每个人可以创建自己的分支,想提交就提交,直到完成所有的功能以后,一次性合并到原来的分支上,这样可以不影响别人工作。Git好就好在切换分支只需要不到1s,比SVN等快很多。创建分支Git默认有一条主分支,即master分支,而HEAD指针实际上指向当前分支的,此时就是master当创建新的分支后,Git新建了一个指针dev,HEAD指向dev,切换到了dev上了。然后后面的修改都是在dev上了,master指针不变。当我们在dev上把工作完成了,就可以合并分支。方法就是直接把master指向dev当前的提交。下面是具体用命令怎么做 创建dev分支并切换 $ git branch dev $ git checkout dev#切换分支 正常提交 切换到master分支上 合并指定分支dev到当前master分支 $ git merge dev 此时有可能两个分支都有提交,所以无法自动合并,需要手动解决冲突,再提交下面是具体用命令怎么做 创建dev分支并切换 $ git branch dev$ git checkout dev#切换分支 正常提交 切换到master分支上 合并指定分支dev到当前master分支 $ git merge dev 此时有可能两个分支都有提交,所以无法自动合并,需要手动解决冲突,再提交 删除分支 $ git branch -d dev 分支策略在实际团队协作的时候,应该保证master非常稳定,只 是用来发布新的版本,平台应该不在上面修改。每个人可以经常生成一些dev分支, 在上面进行修改,当有必要的时候,再合并到主分支上即可。场景一:Bug分支每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。当你接到一个修复一个代号101的bug的任务时,可以创建一个分支issue-101来修复它,但是dev上的工作只进行到一半,还没法提交,怎么办? 可以使用stash功能把现场存储起来,之后可以恢复 $ git stash 然后创建Bug分支,进行修复 切换到master上,合并,删除分支 现在应该回到dev分支继续干活了。恢复现场 git stash apply:恢复以后stash内容不删除,需要再使用git stash drop git stash pop:恢复的时候同时把stash的内容也删除了。 因为可能多次保存现场,所以可以先使用git stash list查看,然后恢复指定的stash $ git stash apply stash@{0} 场景二:Feature分支开发一个新feature,最好新建一个分支,但是还没开发完的时候,收到通知取消此特性。此时当然直接删除就好,不过问题就在于分支还没有被合并,如果删除可以通过git branch -D <name>强行删除。使用标签发布一个版本的时候,可以先在版本库里面打一个tag。标签其实就是版本库的一个快照既然有commit,为什么还要tag呢?比如要将上周一的版本打包发布,我们知道commit id是一堆乱七八糟的数字,并不好查找,如果加上一个tag v1.2,就有了实际意义,可以与某个commit绑在一起,更好查找。如何打标签 首先,切换到打标签的分支上git branch 使用git tag v1.l0创建一个标签,使用git tag可以查看所有标签,标签不是按照时间排序,而是按照字母排序 如果要绑定历史的 commit,可以怎能先使用git log找到commit id git tag v0.9 commit id 可以创建带有说明的标签 git tag -a v0.1 -m "version 0.1 released" 32321332 可以使用 git show v0.1查看说明。如果标签打错了,能改吗?当然能。删除即可。$ git tag -d v0.1 因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。远程仓库到现在为止我们已经学会了如何在自己的电脑上进行工作,本章则将讲解如何把代码托管到Gitee远程仓库来进行管理,这样就可以进行协作以及代码的备份呢。之前我们只是在自己的电脑上搭一个Git仓库,实际上也可以分布到不同的机器上,别的机器只要复制原始版本就好了,这样就可以保证大家都一样。所以完全可以搭一个Git服务器,然后所有的人都从服务器里面复制一份到自己这边,再把各自的提交推送到仓库里面,实现协作。最著名的Git服务器当属GitHub,不过在国内比较的慢,所以我们使用中国版的GitHub——码云(gitee.com)上传公钥本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以需要先上传公钥,想知道原理可以点击HTTPS 首先创建SSH Key 打开CMDer,输入 $ ssh-keygen -t rsa -C "youremail@example.com"#需要把邮件地址换成你自己的邮件地址 然后一路回车 可以通过everything搜索一下id_rsa.pub这个公钥在那里,然后使用Notepad++打开,复制所有的内容。 在Gitee的设置里面粘贴公钥的内容。 为什么要SSH Key呢? 因为码云要识别这个推送确实是你本人干的。当然码云支持多个Key,如果有多个电脑,可以建立多个Key将代码推送到远端如果要把代码推送到远端,有两种场景,一是现在已经在本地建立了仓库了,现在想在Gitee上同样建立一个,然后合并即可。 另一种是现在啥都没干,直接从Gitee上建一个,然后clone到本地即可。首先在Gitee上建立一个新仓库名字是articlespider如果已经在本地建立了仓库 首先关联远程库:git remote add origin git@server-name:path/repo-name.git 使用命令git push -u origin master第一次推送master分支的所有内容; 此后的提交只需要使用git push origin master cd existing_git_repogit remote add origin https://gitee.com/***/articlespider.gitgit push -u origin master如果在新建远程仓库的时候加上了README.MD,但是这个Readme.md又不在本地库里面,所以会报错。可以$ git pull --rebase origin master 当然也可以在本地根目录新建一个Readme.md 如果现在还没有建立仓库,可以使用git clone复制一个版本下来。 $ git clone git@gitee.com:****/articlespider.git 多人协作创建了远程仓库以后,可以进行多人协作。可以将本地的分支推送到远端,也可以从远端拉取推送分支远程仓库默认名称是origin可以使用git push进行推送。$ git push origin 分支 拉取分支现在另一个人需要在dev分支上做开发,首先应该先clone一份到本地。$ git clone git@gitee.com:****/articlespider.git 当从远程仓库克隆的时候,Git自动把master与远程的master对应起来。查看远程库的信息:git remote -v此时只能看到master分支,如果也要在分支dev上开发的话,必须创建远程origin的dev分支到本地,$ git checkout -b dev origin/dev 然后就可以继续开发了。那么我们怎么与之协作呢? 首先可以使用git push origin <branch>推送自己的修改 如果推送失败,则远程分支比本地的更新,需要先用git pull合并。 如果提示no tracking information,说明链接关系没有建立起来。使用 $ git branch --set-upstream <branch> origin/<branch> 如果合并有冲突,则解决冲突,并在本地提交 冲突解决之后,再用git push origin <branch> 推送标签因为创建的标签都只存储在本地,不会自动推送到远程如果要推送某个标签到远程,使用命令git push origin <tagname> 或者,一次性推送全部尚未推送到远程的本地标签:$ git push origin --tags 如果要删除的话,首先需要先删除本地的。$ git tag -d v0.9 然后,从远程删除。$ git push origin :refs/tags/v0.9 修改远程库的名字我们可能同时进行多个项目,他们都需要推送到远端。比如learngit项目,现在要与远程库关联$ git remote add origin git@gitee.com:<your name>/learngit.git 如果报错fatal: remote origin already exists.说明本地库已经关联了一个名叫origin的远程库可以先删除$ git remote rm origin 再关联一个远程库gitee$ git remote add gitee git@gitee.com:<gitee name>/learngit.git 此时远程库的名称叫gitee,不叫origin。如果要推送:$ git push gitee master 配置别名所谓配置别名其实就是配置命令的简写,比如使用git st表示git status等。$ git config --global alias.st status#查看工作区状态 $ git config --global alias.co checkout#切换分支,撤销修改 $ git config --global alias.ci commit#提交 $ git config --global alias.br branch#分支 $ git config --global alias.unstage 'reset HEAD'#回退 $ git config --global alias.last 'log -1'#查看log $git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit" 加上--global是针对当前用户起作用的搭建Git服务器搭建服务器需要准备一台运行Ubuntu的机器 安装git :sudo apt-get install git 创建git 用户,用来运行git服务:sudo adduser git 创建证书登录 收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。 初始化git仓库 选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令: $ sudo git init --bare sample.git 这样就会创建一个没有工作区的裸仓库,把所有者改为git$ sudo chown -R git:git sample.git 禁用shell登录 编辑/etc/passwd文件完成。找到类似下面的一行: $ git:x:1001:1001:,,,:/home/git:/bin/bash 改为:$ git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell 这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。 克隆远程仓库: $ git clone git@server:/srv/sample.git #总结最后把所有的命令总结成一个表格 一级 二级 命令 备注 提交代码 新建仓库 git init 提交到暂存区 git add . 提交到分支 git commit -m "" 查看 查看状态 git status 查看提交记录 git log --pretty=oneline 可以看到commit id 查看命令历史 git reflog 可以看到未来版本 对比 git diff HEAD -- readme.txt 标签 git tag 远端仓库 git remote -v 版本控制 未提交到暂存区 git checkout . 已提交到暂存区 git reset HEAD file 已经提交到分支 git reset --hard <HEAD^> 分支 创建分支 git branch dev 切换分支 git checkout dev 合并 git merge dev 将dev分支合并到当前分支上 删除分支 git branch -d dev 强行删除 git branch -D dev 还没合并就要提前删除 存储现场 git stash 然后切换到另一个分支干活 恢复现场 git stash pop 标签 创建标签 git tag v0.9 删除标签 git tag -d v0.8 推送远程标签 git push origin --tags 删除远程标签 git push origin :refs/tags/v0.9 远程仓库 上传公钥 ssh-keygen -t rsa -C "youremail@example.com" 关联远程库 git remote add origin https://gitee.com/***/articlespider.git 删除远程库 git remote rm origin 第一次提交 git push -u origin master 普通提交 git push origin 拉取 git pull origin 多人协作 复制 git clone git@gitee.com:/articlespider.git 创建远端分支到本地 git checkout -b dev origin/dev 创建链接关系 git branch --set-upstream origin/ 别名 查看log $git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue) 参考本文主要是根据Git教程整理得到的,目的是帮助Git学习。
2017年08月19日
791 阅读
0 评论
0 点赞
2017-08-16
JSON Web Token (JWT)的了解使用
说起JWT,我们先来谈一谈基于token的认证和传统的session认证的区别。基于session认证所显露的问题Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。基于token的鉴权机制基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。流程上是这样的: 用户使用用户名密码来请求服务器 服务器进行验证用户的信息 服务器通过验证发送给用户一个token 客户端存储token,并在每次请求时附送上这个token值 服务端验证token值,并返回数据 这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *。JSON WEB Token(JWT)是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)下面就是一个JWT字符串eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ JWT的头部headerjwt的头部承载两部分信息: 声明类型,这里是jwt 声明加密的算法 通常直接使用 HMAC SHA256 完整的头部就像下面这样的JSON:{ 'typ': 'JWT', 'alg': 'HS256' } 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 payload载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分 标准中注册的声明 公共的声明 私有的声明 标准中注册的声明 (建议但不强制使用) : iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,这个过期时间必须要大于签发时间 nbf: 定义在什么时间之前,该jwt都是不可用的. iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。定义一个payload:{ "sub": "1234567890", "name": "John Doe", "admin": true } 然后将其进行base64加密,得到JWT的第二部分。eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 signatureJWT的第三部分是一个签证信息,这个签证信息由三部分组成: header (base64后的) payload (base64后的) secret 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。// javascript var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 将这三部分用.连接成一个完整的字符串,构成了最终的jwt:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。应用一般是在请求头里加入Authorization,并加上Bearer标注:fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token } }) 服务端会验证token,如果验证通过就会返回相应的资源。 优点 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。 它不需要在服务端保存会话信息, 所以它易于应用的扩展 安全相关 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。 保护好secret私钥,该私钥非常重要。 如果可以,请使用https协议
2017年08月16日
1,642 阅读
0 评论
0 点赞
2017-08-12
Python miniWeb服务器实现
返回固定数据示例代码:import socket def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 2. 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = "are u ok?" response = responseHeader + responseBody clientSocket.send(response.encode("utf-8")) # 3. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 返回固定页面示例代码:import socket def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 2. 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" # 读取固定页面数据 with open('./html/index.html', 'rb') as file: responeBody = file.read() clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responeBody) #先发body # 3. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 解析用户的请求示例代码:import socket import re def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 解析用户的请求示例代码:import socket import re def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 支持默认index页面示例代码:import socket import re def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 多任务协程版示例代码:import socket import re import gevent from gevent import monkey monkey.patch_all() def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 # 创建这个对象之后,gevent就知道了当遇到耗费时间的时候可以切换到这个对象指定的函数中运行 gevent.spawn(serviceClient, clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 面向对象版示例代码:import socket import re import gevent from gevent import monkey monkey.patch_all() class WebServer(object): def __init__(self, port): # 1. 创建套接字 self.tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 self.tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 self.port = port self.tcpSocket.bind(("", self.port)) print("服务器绑定端口为:", self.port) # 3. 设置为监听套接字 self.tcpSocket.listen(128) def StartUp(self): while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = self.tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 # 创建这个对象之后,gevent就知道了当遇到耗费时间的时候可以切换到这个对象指定的函数中运行 gevent.spawn(self.serviceClient, clientSocket) # 6. 关闭套接字 self.tcpSocket.close() def serviceClient(self, clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 创建服务器 server = WebServer(8888) # 启动服务器 server.StartUp() if __name__ == "__main__": main() 命令行参数版示例代码:import socket import re import gevent import sys from gevent import monkey monkey.patch_all() class WebServer(object): def __init__(self, port): # 1. 创建套接字 self.tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 self.tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 self.port = port self.tcpSocket.bind(("", self.port)) print("服务器绑定端口为:", self.port) # 3. 设置为监听套接字 self.tcpSocket.listen(128) def StartUp(self): while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = self.tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 # 创建这个对象之后,gevent就知道了当遇到耗费时间的时候可以切换到这个对象指定的函数中运行 gevent.spawn(self.serviceClient, clientSocket) # 6. 关闭套接字 self.tcpSocket.close() def serviceClient(self, clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): if len(sys.argv) < 2: print('您需要指定监听端口!') return # 获取用户指定的绑定端口 port = int(sys.argv[1]) # 创建服务器 server = WebServer(port) # 启动服务器 server.StartUp() if __name__ == "__main__": main()
2017年08月12日
1,647 阅读
0 评论
0 点赞
2017-08-11
Python socket编程粘包分析
TCP是一种流式连接,数据传输就像一种水流一样,并不区分不同数据包之间的界限。TCP协议允许发送端将几次发送的数据包缓存起来合成一个数据包发送到网络上去,因为这样可以获得更高的效率,这一行为通常是在操作系统提供的SOCKET中实现,所以在应用层对此毫无所觉。 所以我们在程序中调用SOCKET的send发送了数据后操作系统有可能缓存了起来,等待后续的数据一起发送,而不是立即发送出去。当发送网络数据时,tcp协议会根据Nagle算法将时间间隔短,数据量小的多个数据包打包成一个数据包,先发送到自己操作系统的缓存中,然后操作系统将数据包发送到目标程序所对应操作系统的缓存中,最后将目标程序从缓存中取出,而第一个数据包的长度,应用程序并不知道,所以会直接取出数据或者取出部分数据,留部分数据在缓存中,取出的数据可能第一个数据包和第二个数据包粘到一起。当出现这些上面这些情况的时候,接收端就会发现接收到的数据和发送的数据的次数不一致。这个就是粘包现象。粘包解决方案由于应用程序自己发送的数据可以进行打包处理,自己制作协议,对数据进行封装添加报头,然后发送数据部分。而报头必须是固定长度,对方接受时可以先接受报头,对报头进行解析,然后根据报头内的封装的数据的长度对数据进行读取,这样收取的数据就是一个完整的数据包struct模块用处 按照指定格式将Python数据转换为字符串,该字符串为字节流,如网络传输时,不能传输int,此时先将int转化为字节流,然后再发送; 按照指定格式将字节流转换为Python指定的数据类型; 处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理文件; 处理c语言中的结构体; 被struct打包的数据会变成bytes格式,这样便于数据的网络传输发送数据包前 对包的长度进行计算 比较low的方法是 len( package) 然后直接发送给接收端。这样会出现一个问题,就是接收端不知道你的这个 len(package)是几个字节,就也有可能会出现粘包问题。 利用struct对包的长度打包为固定4个字节或8个字节。 struct.pack format参数为"i" 时只能打包长度为10的数字,那么还可以先将 长度 转换为一个json字符串,再打包。
2017年08月11日
2,865 阅读
0 评论
0 点赞
2017-08-11
Python socket套接字之TCP编程
TCP简介TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP通信需要经过创建连接、数据传送、终止连接三个步骤。TCP通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中打电话。TCP特点1)TCP采用发送应答机制TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功。2)超时重传 发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。3)错误校验TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。4)流量控制和阻塞管理流量控制用来避免主机发送得过快而使接收方来不及完全收下。TCP与UDP的不同点 面向连接(确认有创建三方交握,连接已创建才作传输。) 有序数据传输 重发丢失的数据包 舍弃重复的数据包 无差错的数据传输 阻塞/流量控制 TCP通信模型(C/S模型)基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: TCP编程:客户端对于 TCP 客户端编程流程,有点类似于打电话过程: 找个可以通话的手机(socket() ) 拨通对方号码并确定对方是自己要找的人( connect() ) 主动聊天( send() ) 或者,接收对方的回话( recv()) 通信结束后,双方说再见挂电话(close() ) 示例代码:import socket #1、创建TCP套接字 tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #2、链接服务器 tcp.connect(("172.16.1.52", 8888)) #3、发送数据 tcp.send("are u ok?".encode("utf-8")) #4、接收数据,最大一次接收1024个字节 recvData = tcp.recv(1024) print("recvData = ", recvData.decode("utf-8")) #5、关闭套接字 tcp.close() TCP编程:服务器对于 TCP 服务器编程流程,有点类似于接电话过程: 找个可以通话的手机(socket() ) 插上电话卡固定一个号码( bind() ) 职责为被动接听,给手机设置一个铃声来监听是否有来电( listen() ) 有来电,确定双方的关系后,才真正接通不挂电话( accept() ) 接听对方的诉说( recv() ) 适当给些回话( send() ) 通信结束后,双方说再见挂电话( close() ) 示例代码:import socket #1、创建TCP套接字 tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #2、绑定本地网络信息 tcp.bind(('', 8888)) #3、使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了 tcp.listen(128) #4、如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务 # clientSocket用来为这个客户端服务 # tcp专门等待其他新客户端的链接 clientSocket, clientAddr = tcp.accept() #5、接收对方发送过来的数据,一次最大接收1024个字节 recvData = clientSocket.recv(1024) print(clientAddr, " >>>>>>> ", recvData.decode("utf-8")) #6、发送一些数据到客户端 clientSocket.send("thanks".encode("utf-8")) #7、关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接 clientSocket.close() 使用TCP注意点 tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器 tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机 tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务 listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信 关闭accept返回的套接字意味着这个客户端已经服务完毕 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
2017年08月11日
2,126 阅读
0 评论
0 点赞
2017-08-11
Pyhton socket套接字之UDP编程
Pyhton的socket套接字之UDP编程是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的。创建socket 在 Python 中 使用socket 模块的函数 socket 就可以完成:import socket socket.socket(AddressFamily, Type) 函数 socket.socket 创建一个 socket,该函数带有两个参数: AddressFamily:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议) 示例代码:import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #创建udp的套接字 #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #创建tcp的套接字 # ...这里是使用套接字的功能(省略)... # 不用的时候,关闭套接字 s.close() 套接字使用流程与文件的使用流程很类似: 创建套接字 使用套接字收/发数据 关闭套接字 UDP 不提供可靠性的传输,它只是把应用程序传给 IP 层的数据报发送出去,但是并不能保证它们能到达目的地。由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。对于 UDP发送方流程, 有点类似于写信过程: 找个邮政工作人员(socket()) 信封上写上地址同时里面装上信件内容并且投递(sendto() ) 还可以继续写信,或者,接收对方的回信(recvfrom() ) 打完收工(close() ) 示例代码:import socket #1、创建套接字 udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #2、发送数据 sendData = input("请输入要发送的数据:") udp.sendto(sendData.encode("utf-8"), ("172.16.1.52", 8080)) #3、接收数据 recvData = udp.recvfrom(1024) print("recvData = ", recvData) #4、关闭套接字 udp.close() UDP 发送方注意点 本地IP、本地端口(我是谁) 目的IP、目的端口(发给谁) 在发送方的代码中,我们只设置了目的IP、目的端口 发送方的本地 ip、本地 port 是我们调用 sendto 的时候系统底层自动给客户端分配的。分配端口的方式为随机分配,即每次运行系统给的 port 不一样。 python3编码转换字符串通过编码成为字节码,字节码通过解码成为字符串:str -> bytes: encode编码 bytes -> str: decode解码 示例如下:>>> text = '我是文本' >>> text '我是文本' >>> print(text) 我是文本 >>> bytesText = text.encode() >>> bytesText b'\xe6\x88\x91\xe6\x98\xaf\xe6\x96\x87\xe6\x9c\xac' >>> print(bytesText) b'\xe6\x88\x91\xe6\x98\xaf\xe6\x96\x87\xe6\x9c\xac' >>> type(text) <class 'str'> >>> type(bytesText) <class 'bytes'> >>> textDecode = bytesText.decode() >>> textDecode '我是文本' >>> print(textDecode) 我是文本 其中decode()与encode()方法可以接受参数,其声明分别为:bytes.decode(encoding="utf-8", errors="strict") str.encode(encoding="utf-8", errors="strict") 其中的encoding是指在解码编码过程中使用的编码(此处指“编码方案”是名词),errors是指错误的处理方案。详细的可以参照官方文档: str.encode() bytes.decode() 元组拆包>>> addr = ("127.0.0.1", 8888) >>> ip, port = addr >>> type(ip) <class 'str'> >>> type(port) <class 'int'> >>> print(ip) 127.0.0.1 >>> print(port) 8888 示例代码:recvData, recvAddr = udp.recvfrom(1024) print(recvAddr, " >>>>>>>>>>>>> ", recvData.decode("utf-8")) 接收端接收数据的条件UDP网络程序想要收取数据需什么条件? 确定的 ip 地址 确定的端口(port) 这正如,我要收到别人寄过来的信,我必须告诉别人我的地址(ip),同时告诉别人我我的公寓信箱号(端口)。接收端使用 bind() 函数,来完成地址结构与 socket 套接字的绑定,这样 ip、port 就固定了,发送端在 sendto 函数中指定接收端的 ip、port,就可以发送数据了。2.3.4.2 示例代码对于 UDP 服务器编程流程, 有点类似于收信过程: 找个邮政工作人员( socket() ) 确定信箱的位置:地址+信箱号(bind() ) 等待对方的来信( recvfrom() ) 还可以回信(sendto() ),或者,继续等待对方的来信…… 打完收工(close() )
2017年08月11日
1,032 阅读
0 评论
0 点赞
2017-08-10
TCP的三次握手和四次挥手
三次握手在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,采用三次握手建立一个连接。通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。 三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的。但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?举一个现实中例子来解释三次握手。和女朋友异地恋一年多,为了保持感情我提议每天晚上视频聊天一次。有时候聊天的过程中,我的网络或者她的网络可能会不好,视频就会卡住,听不到对方的声音,过一会儿之后才会恢复。中间双方可能就要不断的确认网络是否恢复,但是有时候会:她:“你可以听到了吗?” 我:“可以了,你呢?” 她:“喂喂,你可以听到了吗?” 我:“可以了,我可以听到了,你呢?” 她:“你可以听到了吗?” ..... 这种情况很蛋疼,那么怎样才能找一个简单的办法,让两个人都确认自己可以听到对方的声音,对方也可以听到自己的声音呢?于是有了如下对话:我:1+1等于几? 她:2,2+2等于几? 我:4 四次挥手由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工),正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,保证通信双方都把话说完了才挂电话。套接字端口复用如果客户端和服务器已经连接成功的前提下,通常的情况下,先关闭客户端,再关闭服务器,如果是先关闭服务器,立马启动服务器是,服务器绑定的端口不会立马释放(如下图),要过 1~2 分钟左右才会释放。# 套接字端口复用 tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定信息 tcp.bind(("", 8888))
2017年08月10日
3,079 阅读
0 评论
0 点赞
2017-08-07
Python的全局解释器锁GIL
Python语言和GIL没有半毛钱关系。仅仅是由于历史原因在Cpython虚拟机(解释器),难以移除GIL。GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码。线程释放GIL锁的情况: 在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100 Python使用多进程是可以利用多核的CPU资源的。 多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁 结论:1. 在 处理像科学计算 这类需要持续使用cpu的任务的时候 单线程会比多线程快 2. 在 处理像IO操作等可能引起阻塞的这类任务的时候 多线程会比单线程快当需要执行计算密集型的程序时,可以选择:1. 换解释器 2. 关键地方用C语言是实现 3. .换多进程+协程方案
2017年08月07日
1,564 阅读
0 评论
0 点赞
2017-08-07
Python读取大文件
最近在学习python的过程中接触到了python对文件的读取。python读取文件一般情况是利用open()函数以及read()函数来完成:f = open(filename,'r') f.read() 这种方法读取小文件,即读取远远大小小于内存的文件显然没有什么问题。但是如果是将一个10G大小的日志文件读取,即文件大于内存的大小,这么处理就有问题了,会造成MemoryError ... 也就是发生内存溢出。发生这种错误的原因在于,read()方法执行操作是一次性的都读入内存中,显然文件大于内存就会报错。解决方法:这里发现跟read()类似的还有其他的方法:read(参数)、readline()、readlines()(1)read(参数):通过参数指定每次读取的大小长度,这样就避免了因为文件太大读取出问题。while True: block = f.read(1024) if not block: break (2)readline():每次读取一行while True: line = f.readline() if not line: break (3)readlines():读取全部的行,构成一个list,通过list来对文件进行处理,但是这种方式依然会造成MemoyErrorfor line in f.readlines(): .... 以上基本分析了python中读取文件的方法,但是总感觉不能达到python中所强调的优雅,后来发现了还有下面的解决方法:pythonic(我理解的是很python的python代码)的解决办法:with open(filename, 'r') as flie: for line in file: .... 对可迭代对象file进行迭代,这样会自动的使用buffered IO以及内存管理,这样就不必担心大文件问题了。后来,又发现了一个模块:linecache,这个模块也可以解决大文件读取的问题,并且可以指定读取哪一行,# 输出第2行 text = linecache.getline(filename, 2) 其他for line in fileHandle该方式是最快速的。而且python虚拟机在内部帮助我们对buffer进行管理,内存占用量小,且不差错fileinput方式该方式实际效果较慢,但是也有buffer管理功能
2017年08月07日
994 阅读
0 评论
0 点赞
2017-08-07
BootStrap 前端样式框架学习
BootStrap 前端样式框架 响应式布局就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。这个概念是为解决移动互联网浏览而诞生的。响应式布局可以为不同终端的用户提供更加舒适的界面和更好的用户体验Bootstrap是一个用于构建响应式网站的前端框架,需要比较熟悉HTML+CSS+JavaScript知识, 就能入门Bootstrap布局主要是一个网格系统布局来支持响应式布局,需要学习Bootstrap网格系统布局的原理。网格布局有两种:默认(固定)网格布局和流式网格布局,两者都支持响应式布局。学习Bootstrap布局还有一个特别重要的知识点就是容器布局。响应式布局也有一些必需的标签和文件是肯定要掌握的。Bootstrap主要有下面几部分组成: 12栅格系统——就是将屏幕平分12份(列)。使用行(row)来组织元素(每一行都包括12个列),然后将内容放在列内。通过col-md-offset-*来控制列偏移。 基础布局组件——Bootstrap提供了多种基础布局组件。如排版、代码、表格、按钮、表单等。 Jquery——Bootstrap所有的JavaScript插件都依赖于Jquery的。如果要使用这些JS插件,就必须引用Jquery库。这也是为什么我们在除了要引用Bootstrap的JS文件和CSS文件外,还需要引用Jquery库的原因,两者是依赖关系。 CSS组件——Bootstrap为我们预实现了很多CSS组件。如下拉框、按钮组、导航等。也就是说Bootstrap内容帮我们定义好了很多CSS样式,你可以将这些样式直接应用到之前的下拉框等元素里。 JavaScript插件——Bootstrap也为我们实现了一些JS插件,我们可以用其提供的插件要完成一些常用功能,而不需要我们再重新写JS代码来实现像提示框,模态窗口这样的效果了。 响应式设计——这就是一个设计理念。响应式的意思就是它会根据屏幕尺寸来自动调整页面,使得前端页面在不同尺寸的屏幕上都可以表现很好。 Bootstrap就是由上面几部分组成的12栅格系统Bootstrap定义12栅格系统,就是为了更好的布局。每个前端框架都首先要定义好的就是布局系统。在Bootstrap里面,就是利用行和列来创建页面布局的。其布局有几个原则: 行(row)必须包含在.container(固定宽度)或.container-fluid(100%宽度)中 每行都包含12列 将内容放置在每列中 在bootstrap的栅格系统中,根据宽度将浏览器分为4种。其值分别是:自动(100%)、750px、970px、 1170px。对应的样式为超小(xs)、小型(sm)、中型屏幕 (md)、大型 (lg)其本就是通过媒体查询定义最小宽度实现。所以,Bootstrap做出来的网页向大兼容,向小不兼容!在Bootstrap框架内,已预先定义好了屏幕大小的分界值,其分界值得定义就是通过媒体查询来定义的。其定义方式如下:/* 超小屏幕(手机,小于 768px) */ /* 没有任何媒体查询相关的代码,因为这在 Bootstrap 中是默认的(还记得 Bootstrap 是移动设备优先的吗?) */ /* 小屏幕(平板,大于等于 768px) */ @media (min-width: @screen-sm-min) { ... } /* 中等屏幕(桌面显示器,大于等于 992px) */ @media (min-width: @screen-md-min) { ... } /* 大屏幕(大桌面显示器,大于等于 1200px) */ @media (min-width: @screen-lg-min) { ... } 框架为我们定义了媒体查询,如果超过了媒体查询中定义的最小宽度对应某个类型屏幕,通过这样的方式,就可以在不同屏幕之间收缩元素大小来适应屏幕。Bootstrap提出的概念是移动设备优先的,所以Bootstrap设计出来的页面只能向大兼容,向小不兼容。基础布局组件基础布局组件就是Bootstrap框架内为一些基础布局的标签定义了一些统一的样式。如Table、按钮、表单等。下面让我们看一个Table的例子:<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>Bootstrap 101 Template</title> <!--因为这里没有使用到Bootstrap的JS插件,所以就没有引用Jquery组件--> <!-- Bootstrap --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css"> </head> <body> <h3>默认样式的Table</h3> <table class="table"> <caption>表标题.</caption> <!-- 表头,组合为t+head, t代表table的意思--> <thead> <tr> <th>ID</th> <th>First Name</th> <th>Last Name</th> </tr> </thead> <tbody> <tr> <th scope="row">1</th> <td>Tommy</td> <td>Li</td> </tr> <tr> <th scope="row">2</th> <td>Bob</td> <td>san</td> </tr> <tr> <th scope="row">3</th> <td>Sam</td> <td>wang</td> </tr> </tbody> </table> <h3>带边框的表格</h3> <table class="table-bordered"> <caption>表标题.</caption> <!-- 表头,组合为t+head, t代表table的意思--> <thead> <tr> <th>ID</th> <th>First Name</th> <th>Last Name</th> </tr> </thead> <tbody> <tr> <th scope="row">1</th> <td>Tommy</td> <td>Li</td> </tr> <tr> <th scope="row">2</th> <td>Bob</td> <td>san</td> </tr> <tr> <th scope="row">3</th> <td>Sam</td> <td>wang</td> </tr> </tbody> </table> <!-- 更多表格样式参考: http://v3.bootcss.com/css/#tables--> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> </body> </html> CSS组件CSS组件和基础布局组件差不多,也就是Bootstrap为一些标签定义了一些已有的样式,这些样式运行的效果都非常美观,这样每个公司或开发人员都不需要再去写一篇样式,从而加快开发效率。这里直接看一个导航的例子吧。说白这个东西,你用多了自然就熟了。<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>Bootstrap 101 Template</title> <!-- Bootstrap --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css"> </head> <body> <h3>导航条</h3> <nav class="navbar navbar-default navbar-inverse"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Home</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Home <span class="sr-only">(current)</span></a></li> <li><a href="#">About</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action 1</a></li> <li><a href="#">Action 2</a></li> <li><a href="#">Action 3</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated Action</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> </body> </html> JavaScript插件默认情况下,所有的JS插件都可以通过设置特定的HTML代码和相应的属性来实现,而无需写一行JavaScript代码。其实现本质就是Bootstrap为这些属性实现了对应的JavaScript代码。下面就直接看一个模态窗口的例子:<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>Bootstrap 101 Template</title> <!-- Bootstrap --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css"> </head> <body> <h3>模态窗口</h3> <!-- Button trigger modal --> <button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal"> Click me </button> <div class="modal fade" id="myModal"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title">Modal title</h4> </div> <div class="modal-body"> <p>Test body…</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> </body> </html>
2017年08月07日
1,080 阅读
0 评论
0 点赞
2017-08-07
jQuery 学习笔记
$()函数就是jQuery的核心函数,query就是选择的意思,也就是说jQuery的核心招牌功能就是选择元素:$可以用jQuery来代替,$和jQuery是同一个函数:jQuery选择的元素,所有浏览器兼容!$("#box"):选择页面上id为box的盒子。注意,选择出来的东西,是一个类数组对象,是jQuery自己的对象,这个jQuery对象后面不能跟着原生JS的语法:所以,如果想把jQuery对象,转为原生JS对象,要加[0]就行了。如:$("#box")[0].style.backgroundColor = "red"; 1,选择器注意引号不能丢!!在jQuery世界中,只有三个东西不能加引号,其他必须加引号:1$(this)2$(document)3$(window)上述的三个东西,不能有引号2、CSS函数css函数可以读样式,可以设样式。读样式,可以读取计算后样式,写一个参数,是不是驼峰,无所谓,但是必须加引号:$("p:first").css("background-color"); $("p:first").css("backgroundColor"); (1)设置样式,设置样式,有两种语法,如果你只想设置一个样式,逗号隔开k和v:$("p:odd").css("backgroundColor"**,**"blue"); ( 2)如果想设置很多样式,就写JSON:$("p:lt(4)").css({ "width" : 20, "height" : 20, "backgroundColor" : "red" }) 3、animate函数animate$("p").animate(JS对象, 动画时间, 回调函数/动画类型) 动画$("p").animate({"left":1000},2000,function(){ $(this).css("background-color","red"); }); 和我们封装的框架不一样,jQuery默认有一个处理机制,叫做动画排队。当一个元素接收到了两个animate命令之后,后面的animate会排队: $("p").animate({"left":1000},2000); $("p").animate({"top":400},2000); 先2000毫秒横着跑,然后2000毫秒竖着跑。动画总时长4000。又如:如果想让元素斜着跑,就是同时变化left和top,就写在同一个JSON里面:$("p").animate({"left":1000,"top":400},2000);不同的元素,不排队,是同时的。4、窗口相关原生的:window.onscroll = function(){ console.log(document.body.scrollTop || document.documentElement.scrollTop) } jQuery;窗口卷动值:$(window).scrollTop(); //方法!!不是属性 窗口的宽度:$(window).width(); $(window).height(); 窗口冲锋:$(“html,body”).animate({ //对象变成了$(“html,body”) “scrollTop”: 2000 //变成了属性 },1000);
2017年08月07日
653 阅读
0 评论
0 点赞
1
...
9
10
11
12