diff --git a/2017/03/06/2017-3-6-grails-call-java/index.html b/2017/03/06/2017-3-6-grails-call-java/index.html index 9fbcf7e1..3b362716 100644 --- a/2017/03/06/2017-3-6-grails-call-java/index.html +++ b/2017/03/06/2017-3-6-grails-call-java/index.html @@ -27,8 +27,8 @@ - + @@ -360,10 +360,10 @@
以下Python代码实现对Excel
转存的csv文件进行读取。
|
把csv
文件入库是一件脏活。表面上看csv
文件是一个非常简单的
+逗号分隔符文件。但是其实不然。Excel
转存的csv
文件并不是标准的以逗号作为分隔符,
+并且对所有的项用双引号包裹。现在我就遇到了从Oracle
导出的csv
文件,以上的代码
+不起作用了。
究竟怎么回事呢,找了一圈也没有发现使用pandas.read_csv()
读取
+这种标准csv文件的方法。还是先把问题简化一下,看看Python
的csv模块
是如何读取的吧。
+简单的查找就可以找到答案。
|
好的,pandas.read_csv()
肯定是要调用csv模块的,那么看看
+它的方法参数表吧。
++pandas.read_csv(filepath_or_buffer, sep=’, ‘, delimiter=None, header=’infer’, names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression=’infer’, thousands=None, decimal=’.’, lineterminator=None, quotechar=’”‘, quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=False, error_bad_lines=True, warn_bad_lines=True, skipfooter=0, skip_footer=0, doublequote=True, delim_whitespace=False, as_recarray=False, compact_ints=False, use_unsigned=False, low_memory=True, buffer_lines=None, memory_map=False, float_precision=None)[source]¶
+
真是够长的,还是搜一下有没有dialect=
,呵呵,果然有。
++dialect : str or csv.Dialect instance, default None
+
+ If None defaults to Excel dialect. Ignored if sep longer than 1 char See csv.Dialect documentation for more details
那么问题解决了。把第一段代码改改吧。
+
|
一运行还是报错,这是怎么回事呢,编码换成utf8
,也不行。最后才发现
+需要使用gb18030
才行。即使使用chardet
的编码探测模块,也不一定能探测出来,因为整个文档
+只有少数字符是超出了gbk
,所以不可能既高效又准确的解决这个问题。
以下Python代码实现对Excel
转存的csv文件进行读取。
|
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
+Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.
+Example:
+Given nums = [-2, 5, -1], lower = -2, upper = 2,
+Return 3.
+The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.
+
-把csv
文件入库是一件脏活。表面上看csv
文件是一个非常简单的
-逗号分隔符文件。但是其实不然。Excel
转存的csv
文件并不是标准的以逗号作为分隔符,
-并且对所有的项用双引号包裹。现在我就遇到了从Oracle
导出的csv
文件,以上的代码
-不起作用了。
究竟怎么回事呢,找了一圈也没有发现使用pandas.read_csv()
读取
-这种标准csv文件的方法。还是先把问题简化一下,看看Python
的csv模块
是如何读取的吧。
-简单的查找就可以找到答案。
|
最直接的想法当然就是遍历求和了。但是这样是无法通过测试的。
+
|
好的,pandas.read_csv()
肯定是要调用csv模块的,那么看看
-它的方法参数表吧。
--pandas.read_csv(filepath_or_buffer, sep=’, ‘, delimiter=None, header=’infer’, names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression=’infer’, thousands=None, decimal=’.’, lineterminator=None, quotechar=’”‘, quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=False, error_bad_lines=True, warn_bad_lines=True, skipfooter=0, skip_footer=0, doublequote=True, delim_whitespace=False, as_recarray=False, compact_ints=False, use_unsigned=False, low_memory=True, buffer_lines=None, memory_map=False, float_precision=None)[source]¶
-
真是够长的,还是搜一下有没有dialect=
,呵呵,果然有。
--dialect : str or csv.Dialect instance, default None
-
- If None defaults to Excel dialect. Ignored if sep longer than 1 char See csv.Dialect documentation for more details
那么问题解决了。把第一段代码改改吧。
-
|
必须要想出复杂度是Nlog(N)
的算法才行。但是问题也很明显,
+要想找出所有的s(i,j) for 0 <= i <= j < n and lower <= s(i,j) <= upper
+那必须要进行遍历啊,因此时间复杂度必须是N^2
。
+啊啊,实在想不出来了。
第二天,既然要求时间复杂度为NlogN
那么就必然不能遍历求出s(i,j)
,但是
+题目要求的是求出Num(s(i,j)) for 0 <= i <= j < n and lower <= s(i,j) <= upper
+,也就是说给出个数就行,因此确实不一定要给出所有的s(i,j)
。嗯,有点进展了。
第三天,排序算法的复杂度是NlogN
,怎么才能依靠排序算法求出呢。难道对所有的
+s(i,j)
进行排序么?这样肯定不行。
+能否使用老办法画图呢?这样好观察一下。但是仔细想了想,这是一个三维图。
+纸上画不出来。
+只好画一个二维图吧。下图是对数组[5,7,8,-2,1]
画的二维表格。格子
+中的数字就是s(i,j)
。
我仔细的盯着看。我给这种解决问题的方式,起了
+个名字——死亡凝视
。看过The Big Bang
的朋友可能记得Sheldon
和Raj
一起
+工作的样子,就是一动不动的看着黑板上的公式,看上一整天。
嘿嘿,你们不要笑啊,我的方式差不多。只不过是盯着表格。这样的表格 +其实我画了很多。但是只有这个我看出了规律。
+ +看出来了么,三列带颜色的数字。是不是发现它们有相同的趋势呢? +是的它们有相同的趋势。
+如果没有看出来。我再画一个图。看看这三列数字和蓝色数字的关系。
+ +是的。20 - 5 = 15
,18 - 5 = 13
,19 - 5 = 14
。
+假如上下区间为[12,18]
,那么对于排序后的第一列[5,12,18,19,20]
,
+可以使用二分查找法找出上限的位置是2,下限的位置为1,个数为2。
+那么第二列[7,15,13,14]
在[12,18]
的数字的个数为3。
+怎么根据第一列排序后的数字求出呢。嗯,很简单。
+我们把上下限加上5,变成17, 23
,这样就可以发现[5,12,18,19,20]
找出
+个数为3。嗯,哈哈。真是不错。这样子,就不需要求出所有的s(i,j)
了。
但是,别着急,还是有问题,我们需要在计算第二列的个数的之前,把第一列的数字中的5
排除掉。
+否则的话,如果区间是[-100,100]
,那么我们就多计算了。
|
一运行还是报错,这是怎么回事呢,编码换成utf8
,也不行。最后才发现
-需要使用gb18030
才行。即使使用chardet
的编码探测模块,也不一定能探测出来,因为整个文档
-只有少数字符是超出了gbk
,所以不可能既高效又准确的解决这个问题。
排序操作的时间复杂度是O(NlogN)
,而找出每列一列的个数的时间复杂度是O(logN)
+因为一共有N列,所以时间复杂度为O(NlogN)
,另外
+我们在代码中使用了对List
数据的非尾部的pop
操作,这个操作的时间
+复杂度接近O(N)
,严格来说,我们的代码的时间复杂度还是O(N^2)
。如果能够不使用这个操作。
+我们的代码速度会更快。
l.pop(pos) # <-- 这里是个接近O(N)的操作
+
]]>You are given an integer array nums and you
+have to return a new counts array.
+The counts array has the property where counts[i] is
+the number of smaller elements to the right of nums[i].
+
+Example:
+
+Given nums = [5, 2, 6, 1]
+
+To the right of 5 there are 2 smaller elements (2 and 1).
+To the right of 2 there is only 1 smaller element (1).
+To the right of 6 there is 1 smaller element (1).
+To the right of 1 there is 0 smaller element.
+Return the array [2, 1, 1, 0].
+
+
+
+拿到这个题,最先想到的是时间复杂度O(N*N) +的解法。那就实现一下吧。
+
|
把上面的代码提交上去,还是正确的,但是超时了。 +该怎么优化呢?
+遇到这样的问题,如果能够想出S(N)=f(S(N-1),S(N-2),...)
这样的动态规划
+解法
+就可以把时间复杂度降到O(N)了。能不能使用动态规划呢?
+还是要仔仔细细列出来,可是怎么也找不到递推规律。只好放弃。不过在思考
+的过程中忽然想到一种思路。对于数组[5, 2, 6, 1]]
,如果我先遍历
+求出5
的smaller数组[2,1]
和larger数组[6]
。那么6
的smaller个数怎么
+求呢,s(6) = len([5,2,6,1]) - pos(6) = 4-3 = 1
。看起来很不错,但是当
+数的个数多起来,就变得复杂了,无法这样简单的求解了。真是非常的泄气啊。
+想了好久也没有想出新点子。脑子里把曾经用到的方法想了一个遍,
+也还是不行。
物理学科中经常使用的一个策略就是转换坐标系,这样可以神奇的
+化繁为简。那么我是否可以这样做呢。看看能不能用二维的图来重新展示这个问题。
+于是我就画了这样一个图表。这是对数组[5,1,6,7,2,3]
画的表格。横轴表示数组的index,
+纵轴表示大小。
我把它画在纸上,盯着看了半小时。呵呵,真的有这么长时间。 +因为我一直还是老思路,总想着根据第一个数字把剩下的数组分成大小两批。 +后来我想,既然是二维图,我既可以从左向右,一列一列的看,也可以从上往下一行一行的 +看。 不错,终于有了新的思路。
+value = smaller_num(Nmax) = 数组的长度 - pos(Nmax)
smaller[index(Nmax)] = value
如下图。首先找到最大的数字7
,它的索引是4
(第四个数字),
+而数组的长度为6
,那么smaller_num("7") = 6 - 4 = 2
。然后不考虑该数字7
。
+对7
右侧的2
和3
同时向左移动,同时令数组的长度减一。然后依次这样操作。
这样我们就可以写出一个新的算法。而对于步骤5则
+使用了简单实现,遍历整个数组,对所有index > index(Nmax)
数的索引进行
+减一。上述算法描述没有考虑有多个最大值的情况。但是下面的代码里考虑了。
|
事情终于有了进展,看起来很不错。但是仍然 +遇到了时间复杂度过高的问题。因为实现步骤5的方法导致整个程序的 +时间复杂度是O(N*N)。那么有什么办法能够优化呢。嗯,肯定有办法的。 +我们这样来做。
+(origin_index,value)
的origin
List。(origin_index,value)
的l
List。l
根据value
从大到小进行遍历:根据orgin_index使用二分查找
+查找origin
中对应的实际索引,计算得到该值对应的smaller_num之后,
+从origin
中删除该项。因为二分查找和删除项的时间复杂度是O(logN)。 +上述步骤的总的时间复杂度是O(N*logN)。
+而相等的数怎么办呢?嗯我们可以在排序 +的时候保证序列不但满足值的升序,还能保证索引值的升序。 +这样,原始数组中处于最右侧的最大数永远在序列的末尾。这个数的右侧不会有相等的值。 +因而计算的时候就不需要考虑相等值的情况。 +而该数字计算之后会进行删除。那么即使有相等的数,也不会受到影响。 +真是很不错啊。
+
|
以上的解法是首先排序,再进行巧妙处理的一种方法。排序的
+时间复杂度是O(N*logN)
,接下来的处理也是O(N*logN)
。
You are given an integer array nums and you
-have to return a new counts array.
-The counts array has the property where counts[i] is
-the number of smaller elements to the right of nums[i].
-
-Example:
-
-Given nums = [5, 2, 6, 1]
-
-To the right of 5 there are 2 smaller elements (2 and 1).
-To the right of 2 there is only 1 smaller element (1).
-To the right of 6 there is 1 smaller element (1).
-To the right of 1 there is 0 smaller element.
-Return the array [2, 1, 1, 0].
-
-
-
-拿到这个题,最先想到的是时间复杂度O(N*N) -的解法。那就实现一下吧。
-
|
把上面的代码提交上去,还是正确的,但是超时了。 -该怎么优化呢?
-遇到这样的问题,如果能够想出S(N)=f(S(N-1),S(N-2),...)
这样的动态规划
-解法
-就可以把时间复杂度降到O(N)了。能不能使用动态规划呢?
-还是要仔仔细细列出来,可是怎么也找不到递推规律。只好放弃。不过在思考
-的过程中忽然想到一种思路。对于数组[5, 2, 6, 1]]
,如果我先遍历
-求出5
的smaller数组[2,1]
和larger数组[6]
。那么6
的smaller个数怎么
-求呢,s(6) = len([5,2,6,1]) - pos(6) = 4-3 = 1
。看起来很不错,但是当
-数的个数多起来,就变得复杂了,无法这样简单的求解了。真是非常的泄气啊。
-想了好久也没有想出新点子。脑子里把曾经用到的方法想了一个遍,
-也还是不行。
物理学科中经常使用的一个策略就是转换坐标系,这样可以神奇的
-化繁为简。那么我是否可以这样做呢。看看能不能用二维的图来重新展示这个问题。
-于是我就画了这样一个图表。这是对数组[5,1,6,7,2,3]
画的表格。横轴表示数组的index,
-纵轴表示大小。
我把它画在纸上,盯着看了半小时。呵呵,真的有这么长时间。 -因为我一直还是老思路,总想着根据第一个数字把剩下的数组分成大小两批。 -后来我想,既然是二维图,我既可以从左向右,一列一列的看,也可以从上往下一行一行的 -看。 不错,终于有了新的思路。
-value = smaller_num(Nmax) = 数组的长度 - pos(Nmax)
smaller[index(Nmax)] = value
如下图。首先找到最大的数字7
,它的索引是4
(第四个数字),
-而数组的长度为6
,那么smaller_num("7") = 6 - 4 = 2
。然后不考虑该数字7
。
-对7
右侧的2
和3
同时向左移动,同时令数组的长度减一。然后依次这样操作。
这样我们就可以写出一个新的算法。而对于步骤5则
-使用了简单实现,遍历整个数组,对所有index > index(Nmax)
数的索引进行
-减一。上述算法描述没有考虑有多个最大值的情况。但是下面的代码里考虑了。
|
事情终于有了进展,看起来很不错。但是仍然 -遇到了时间复杂度过高的问题。因为实现步骤5的方法导致整个程序的 -时间复杂度是O(N*N)。那么有什么办法能够优化呢。嗯,肯定有办法的。 -我们这样来做。
-(origin_index,value)
的origin
List。(origin_index,value)
的l
List。l
根据value
从大到小进行遍历:根据orgin_index使用二分查找
-查找origin
中对应的实际索引,计算得到该值对应的smaller_num之后,
-从origin
中删除该项。因为二分查找和删除项的时间复杂度是O(logN)。 -上述步骤的总的时间复杂度是O(N*logN)。
-而相等的数怎么办呢?嗯我们可以在排序 -的时候保证序列不但满足值的升序,还能保证索引值的升序。 -这样,原始数组中处于最右侧的最大数永远在序列的末尾。这个数的右侧不会有相等的值。 -因而计算的时候就不需要考虑相等值的情况。 -而该数字计算之后会进行删除。那么即使有相等的数,也不会受到影响。 -真是很不错啊。
-
|
以上的解法是首先排序,再进行巧妙处理的一种方法。排序的
-时间复杂度是O(N*logN)
,接下来的处理也是O(N*logN)
。
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
-Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.
-
-Example:
-Given nums = [-2, 5, -1], lower = -2, upper = 2,
-Return 3.
-The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.
-
-
-
-最直接的想法当然就是遍历求和了。但是这样是无法通过测试的。
-
|
必须要想出复杂度是Nlog(N)
的算法才行。但是问题也很明显,
-要想找出所有的s(i,j) for 0 <= i <= j < n and lower <= s(i,j) <= upper
-那必须要进行遍历啊,因此时间复杂度必须是N^2
。
-啊啊,实在想不出来了。
第二天,既然要求时间复杂度为NlogN
那么就必然不能遍历求出s(i,j)
,但是
-题目要求的是求出Num(s(i,j)) for 0 <= i <= j < n and lower <= s(i,j) <= upper
-,也就是说给出个数就行,因此确实不一定要给出所有的s(i,j)
。嗯,有点进展了。
第三天,排序算法的复杂度是NlogN
,怎么才能依靠排序算法求出呢。难道对所有的
-s(i,j)
进行排序么?这样肯定不行。
-能否使用老办法画图呢?这样好观察一下。但是仔细想了想,这是一个三维图。
-纸上画不出来。
-只好画一个二维图吧。下图是对数组[5,7,8,-2,1]
画的二维表格。格子
-中的数字就是s(i,j)
。
我仔细的盯着看。我给这种解决问题的方式,起了
-个名字——死亡凝视
。看过The Big Bang
的朋友可能记得Sheldon
和Raj
一起
-工作的样子,就是一动不动的看着黑板上的公式,看上一整天。
嘿嘿,你们不要笑啊,我的方式差不多。只不过是盯着表格。这样的表格 -其实我画了很多。但是只有这个我看出了规律。
- -看出来了么,三列带颜色的数字。是不是发现它们有相同的趋势呢? -是的它们有相同的趋势。
-如果没有看出来。我再画一个图。看看这三列数字和蓝色数字的关系。
- -是的。20 - 5 = 15
,18 - 5 = 13
,19 - 5 = 14
。
-假如上下区间为[12,18]
,那么对于排序后的第一列[5,12,18,19,20]
,
-可以使用二分查找法找出上限的位置是2,下限的位置为1,个数为2。
-那么第二列[7,15,13,14]
在[12,18]
的数字的个数为3。
-怎么根据第一列排序后的数字求出呢。嗯,很简单。
-我们把上下限加上5,变成17, 23
,这样就可以发现[5,12,18,19,20]
找出
-个数为3。嗯,哈哈。真是不错。这样子,就不需要求出所有的s(i,j)
了。
但是,别着急,还是有问题,我们需要在计算第二列的个数的之前,把第一列的数字中的5
排除掉。
-否则的话,如果区间是[-100,100]
,那么我们就多计算了。
|
排序操作的时间复杂度是O(NlogN)
,而找出每列一列的个数的时间复杂度是O(logN)
-因为一共有N列,所以时间复杂度为O(NlogN)
,另外
-我们在代码中使用了对List
数据的非尾部的pop
操作,这个操作的时间
-复杂度接近O(N)
,严格来说,我们的代码的时间复杂度还是O(N^2)
。如果能够不使用这个操作。
-我们的代码速度会更快。
l.pop(pos) # <-- 这里是个接近O(N)的操作
-
-]]>官方readme
给出了办法来证明运行成功。执行java -cp clojure.jar clojure.lang.Repl
-,这样就可以进入clojure的repl环境。
d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Repl
-Clojure
-user=> (def n 2)
-#'user/n
-user=> (* n 2)
-4
-user=>
-
-如何运行一个clj文件呢?可以这样做。
-(print "hello world")
java -cp clojure.jar clojure.lang.Script hello.clj
如下所示。
-d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Script hello_world.clj
-hello world
-
-成功了。
-]]> -我的Grails
的环境是
grails -v
-| Grails Version: 3.2.6
-| Groovy Version: 2.4.7
-| JVM Version: 1.8.0_71
-
-
-
-grails 3.2.6是用gradle进行构建的。所以如果要添加java类,
-就需要修改build.gradle
。
创建src/main/java
目录。对于com.yanggeorge.XMLtest
类,
-则要创建src/main/java/com/yanggeorge/
目录,并把XMLtest.java放在该
-路径下。
在build.gradle
文件中添加如下代码
|
编译XMLtest.java。可以用grails compile
进行编译。
D:\work\grails>grails compile
-:compileJava UP-TO-DATE
-:compileGroovy UP-TO-DATE
-:buildProperties
-:processResources UP-TO-DATE
-:classes UP-TO-DATE
-
-BUILD SUCCESSFUL
-
-Total time: 23.638 secs
-D:\work\grails>
-
-修改grails-app/conf/spring/resources.groovy
|
已经可以使用myXMLtest
了,例如创建一个service,grails-app/services/rss/RssService.groovy
-第6行就是依赖注入的bean。
|
官方readme
给出了办法来证明运行成功。执行java -cp clojure.jar clojure.lang.Repl
+,这样就可以进入clojure的repl环境。
d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Repl
+Clojure
+user=> (def n 2)
+#'user/n
+user=> (* n 2)
+4
+user=>
+
+如何运行一个clj文件呢?可以这样做。
+(print "hello world")
java -cp clojure.jar clojure.lang.Script hello.clj
如下所示。
+d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Script hello_world.clj
+hello world
+
+成功了。
]]>我的Grails
的环境是
grails -v
+| Grails Version: 3.2.6
+| Groovy Version: 2.4.7
+| JVM Version: 1.8.0_71
+
+
+
+grails 3.2.6是用gradle进行构建的。所以如果要添加java类,
+就需要修改build.gradle
。
创建src/main/java
目录。对于com.yanggeorge.XMLtest
类,
+则要创建src/main/java/com/yanggeorge/
目录,并把XMLtest.java放在该
+路径下。
在build.gradle
文件中添加如下代码
|
编译XMLtest.java。可以用grails compile
进行编译。
D:\work\grails>grails compile
+:compileJava UP-TO-DATE
+:compileGroovy UP-TO-DATE
+:buildProperties
+:processResources UP-TO-DATE
+:classes UP-TO-DATE
+
+BUILD SUCCESSFUL
+
+Total time: 23.638 secs
+D:\work\grails>
+
+修改grails-app/conf/spring/resources.groovy
|
已经可以使用myXMLtest
了,例如创建一个service,grails-app/services/rss/RssService.groovy
+第6行就是依赖注入的bean。
|
如何更改镜像文件的大小呢,尤其是如何把一个固定大小的镜像变大,并且之前的内容完全不改变。 +我尝试了一些方法,比如下面的几个步骤。1.查看信息,2.克隆新的vdi,3.调整大小,4.再查看信息。
+ + +++VBoxManage showhdinfo mydisk.vdi
+
VBoxManage clonehd mydisk.vdi mydiskClone.vdi
VBoxManage modifyhd mydiskClone.vdi –resize 61440
VBoxManage showhdinfo mydiskClone.vdi
这个方法的确是调整了大小,不过镜像文件变成了dynamic size
而不是fixed size
。这还好了,
+最麻烦的是,当使用新的vdi的时候,发现自己的用户已经无法登陆,只能使用root用户登陆了。
+这是无法接受的。
幸运的是有人给出了详细的方法。
++ ++
我来解释一下,其中最核心的就是
+dd if=/dev/sda of=/dev/sdb
+
+执行完成这个语句之后,不要进行分区等任何操作。 +(虽然看到新增加的磁盘处于未分配的状态。)直接单独挂载这个磁盘启动。 +果然可以登陆,一切照旧。但是只是这样并没有完成磁盘空间变大。只是实现了内容的复制。 +要想扩大,就需要使用GParted进行resize了。重新使用GParted Live CD进入新磁盘的分区图形界面, +可以发现新磁盘的某些空间已经进行分区,但是还有部分空间没有进行分区。 +在我这里是这样显示的
++ ------------------------------------------------------ + | sda | swap | unallocated | + ------------------------------------------------------ ++ +
最后重启。应该会发现无法登陆,出现这样一个提示
+a start job is running for dev-disk-by…
。耐心等待一下,发现还是可以正常登陆的。
+而且磁盘的空间变大了。一切似乎不错。
重启一下看看,发现还是有a start job is running for dev-disk-by…
这个提示,
+需要等待一两分钟,真是糟糕。别担心,这是因为重新创建了swap交换分区的缘故。有人已经给出了解决办法。
++ +To avoid the issue, in /etc/fstab you can either +Replace the swap UUID with the new one (run sudo blkid to find it) after the primary partition resizing.
+
重启,问题解决了。
+]]>如何更改镜像文件的大小呢,尤其是如何把一个固定大小的镜像变大,并且之前的内容完全不改变。 -我尝试了一些方法,比如下面的几个步骤。1.查看信息,2.克隆新的vdi,3.调整大小,4.再查看信息。
- - ---VBoxManage showhdinfo mydisk.vdi
-
VBoxManage clonehd mydisk.vdi mydiskClone.vdi
VBoxManage modifyhd mydiskClone.vdi –resize 61440
VBoxManage showhdinfo mydiskClone.vdi
这个方法的确是调整了大小,不过镜像文件变成了dynamic size
而不是fixed size
。这还好了,
-最麻烦的是,当使用新的vdi的时候,发现自己的用户已经无法登陆,只能使用root用户登陆了。
-这是无法接受的。
幸运的是有人给出了详细的方法。
-- --
我来解释一下,其中最核心的就是
-dd if=/dev/sda of=/dev/sdb
-
-执行完成这个语句之后,不要进行分区等任何操作。 -(虽然看到新增加的磁盘处于未分配的状态。)直接单独挂载这个磁盘启动。 -果然可以登陆,一切照旧。但是只是这样并没有完成磁盘空间变大。只是实现了内容的复制。 -要想扩大,就需要使用GParted进行resize了。重新使用GParted Live CD进入新磁盘的分区图形界面, -可以发现新磁盘的某些空间已经进行分区,但是还有部分空间没有进行分区。 -在我这里是这样显示的
-- ------------------------------------------------------ - | sda | swap | unallocated | - ------------------------------------------------------ -- -
最后重启。应该会发现无法登陆,出现这样一个提示
-a start job is running for dev-disk-by…
。耐心等待一下,发现还是可以正常登陆的。
-而且磁盘的空间变大了。一切似乎不错。
重启一下看看,发现还是有a start job is running for dev-disk-by…
这个提示,
-需要等待一两分钟,真是糟糕。别担心,这是因为重新创建了swap交换分区的缘故。有人已经给出了解决办法。
-- -To avoid the issue, in /etc/fstab you can either -Replace the swap UUID with the new one (run sudo blkid to find it) after the primary partition resizing.
-
重启,问题解决了。
-]]>两个步骤,
参考了Bittorrent udp-tracker protocol extension -因为对入参和返回参数说明的更详细一些。用的tracker是一个热门的磁力链接。
-ip = 'exodus.desync.com' # get from tracker list
-port = 6969 # get from tracker list
-
-而info_hash是用uTorrent软件打开该磁力链接后获得的。
-1aa4c13830b822c1375686d685a9fce23405f6ba
-
-以下是代码实现。uTorrent软件打开磁力链接会拿到tracker列表。
-announce的重要参数说明
-一旦发布完成,该监听的端口会收到DHT协议的请求,例如FIND_NODE
-
|
运行日志如下,可以看到拿到了200个peers的ipv4地址和端口。
-
|
BEP3的peer message章节说明了几种消息类型。 -其中bitfield消息理解起来是容易的,但是实际过程中却略有不同。
- - ---‘bitfield’ is only ever sent as the first message. Its payload is a bitfield with each index that downloader has sent set to one and the rest set to zero. Downloaders which don’t have anything yet may skip the ‘bitfield’ message. The first byte of the bitfield corresponds to indices 0 - 7 from high bit to low bit, respectively. The next one 8-15, etc. Spare bits at the end are set to zero.
-
这段话的意思很简单,举例说明一下,如果一个文件有10个分片,那么如果没有下载任何数据,那么bitfield的值应该是(为了便于阅读每个字节的二进制码 以一个空格键隔开)
-0b0000 0000 0000
-
-如果下载了piece 0, 则值应该是
-0b1000 0000 0000
-
-如果全部下载完成,则应该是
-0b1111 1111 1100
-
-一共是10个1,表示10个piece。
-但是实际上呢,对于一个下载完成的文件,获取到的bitfield值是0b111111111100
么?
我们可以自己制作一个种子来验证一下。
-用uTorrent软件选择本地的一个文件制作一个种子文件。如下图所示
- -该文件有110个片。
-我们读取代码如下
-
|
我们看到日志如下
-/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
-sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
-Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xab\x9c\xa8$G\x8euX\x91\xad\x9e')
-id=20
-recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
-id=5
-recv=b'\xff\xff\xffg~\xaf\xbf=\x7f\xff\xeb&\xdd\x98'
-1111111111111111111111110110011101111110101011111011111100111101011111111111111111101011001001101101110110011000
+ANNOUNCE,拿着connection_id和info_hash等参数再请求。
+
+参考了Bittorrent udp-tracker protocol extension
+因为对入参和返回参数说明的更详细一些。用的tracker是一个热门的磁力链接。
+ip = 'exodus.desync.com' # get from tracker list
+port = 6969 # get from tracker list
-这是怎么回事,为什么中间会有这么多0呢,不是已经下载完成了么?
-111111111111111111111111011001110111111010101111101111110011110...
+而info_hash是用uTorrent软件打开该磁力链接后获得的。
+1aa4c13830b822c1375686d685a9fce23405f6ba
-应该是110个1才对。而且如果再运行几遍会发现每次有不同的index的片是0。
-have消息
原来这是uTorrent软件实现的一个策略,即使已经下载完成,也不会返回110个1的bitfield。而是用have消息
来
-补全,也就是id为4的peer消息。have消息的值是一个piece的index值,表示该piece下载完成。
-因此,我们需要根据bitfield消息和have消息来看是否下载完成。代码修改一下,每接收到一个have消息就把相应的index
-置为1。
-from ym.bt.peer_protocol import pack_handshake, unpack_handshake, sendall, recv, pack_extend
from ym.bt.parse_torrent import BDecode
from ym.bt.udp_announce import ip_me
import socket
import struct
import logging
from bitstring import BitArray, Bits
log = logging.getLogger()
def download():
try:
info_hash = 'c99c3b7a5ba31a8966e6c9a40bc4f83887a107e5'
ip, port = ip_me(), 40959
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
sock.connect((ip, port))
handshake = pack_handshake(info_hash)
rt = sendall(sock, handshake)
if rt is None:
return
print("sent={}, data={}".format(rt, handshake))
data = recv(sock, 68)
tup = unpack_handshake(data)
if tup is None:
return
print(tup)
# interested
data = struct.pack(">i B", 1, 2)
rt = sendall(sock, data)
if rt is None:
return
bitfield = None
while True:
data = recv(sock, 4)
if data is None:
return
num, = struct.unpack(">i", data)
tmp = recv(sock, 1)
if tmp is None:
return
id, = struct.unpack(">B", tmp)
print("id={}".format(id))
tmp = recv(sock, int(num) - 1)
print("recv={}".format(tmp))
if id == 5:
# bitfield
bitfield = BitArray(tmp)
print(bitfield.bin)
elif id == 4:
# have
index, = struct.unpack(">i", tmp)
print("index={}".format(index))
if bitfield.bin[index] == '0':
bitfield.set('1', index)
print(bitfield.bin)
else:
pass
except Exception as e:
log.exception(e)
if __name__ == '__main__':
download()
-
-我们再看一下日志
-/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
-sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
-Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xabY\x08\r\x82=\xd1J^\x930')
-id=20
-recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
-id=5
-recv=b'\xfb\xdf\xbf\xee\xdf\xdf\x9d\xef\xb9\xfa\xfbv^\xf8'
-1111101111011111101111111110111011011111110111111001110111101111101110011111101011111011011101100101111011111000
-id=4
-recv=b'\x00\x00\x002'
-index=50
-1111101111011111101111111110111011011111110111111011110111101111101110011111101011111011011101100101111011111000
-
-...
-...
-...
-
-id=4
-recv=b'\x00\x00\x00b'
-index=98
-1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111100
-id=4
-recv=b'\x00\x00\x00X'
-index=88
-1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
-id=4
-recv=b'\x00\x00\x00\n'
-index=10
-1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
-already recv:b''
-timed out
-Traceback (most recent call last):
- File "/Users/ym/charm/pytest/ym/bt/peer_protocol.py", line 40, in recv
- tmp = sock.recv(n - len(data))
-socket.timeout: timed out
+以下是代码实现。uTorrent软件打开磁力链接会拿到tracker列表。
+announce的重要参数说明
+
+- num_want : -1 # 希望获得的peer个数,则是默认的peer个数
+- ip : 0 # 当前发送udp包的地址
+- port : 监听的公网端口
+
+一旦发布完成,该监听的端口会收到DHT协议的请求,例如FIND_NODE
+# -*- coding: utf-8 -*-
__author__ = 'ym'
"""
Date : '2019/1/8'
Description : udp tracker protocol
"""
import socket
import struct
def ip_me():
import psutil
import socket
def get_ip_addresses(family):
for interface, snics in psutil.net_if_addrs().items():
for snic in snics:
if snic.family == family:
yield (interface, snic.address)
addrs = list(get_ip_addresses(socket.AF_INET))
for tup in addrs:
if tup[0] == 'en0':
return tup[1]
SOURCE_IP = ip_me()
SOURCE_PORT = 59876
def to_byte(integer, length=4):
return integer.to_bytes(length, byteorder='big')
def udp_send(ip, port, data):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((SOURCE_IP, SOURCE_PORT))
sock.sendto(data, (ip, port))
def udp_recv(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((ip, port))
while True:
data, addr = sock.recvfrom(2048)
print("received message:", data)
return data
class UdpAnnouceRequest(object):
def __init__(self):
self.connection_id = None
self.action = None
self.transaction_id = None
self.info_hash = None
self.peer_id = None
self.downloaded = None
self.left = None
self.uploaded = None
self.event = 0
self.ip = None
self.key = None
self.num_want = -1
self.port = None
self.extensions = None
def get_bytes(self):
result = to_byte(self.connection_id, 8)
result += to_byte(self.action)
result += to_byte(self.transaction_id)
result += self.info_hash
result += self.peer_id
result += to_byte(self.downloaded, 8)
result += to_byte(self.left, 8)
result += to_byte(self.uploaded, 8)
result += to_byte(self.event)
result += to_byte(self.ip)
result += to_byte(self.key)
result += struct.pack(">i", self.num_want)
result += to_byte(self.port, 2)
return result
def udp_announcing(request: UdpAnnouceRequest, dst_ip, dst_port):
data = request.get_bytes()
udp_send(dst_ip, dst_port, data)
def connect(tracker_ip, tracker_port):
protocol_id = 0x41727101980 # magic number
action = 0 # connect
transaction_id = 123 # random
data = struct.pack(">Qii", protocol_id, action, transaction_id)
udp_send(tracker_ip, tracker_port, data)
reply = udp_recv(SOURCE_IP, SOURCE_PORT)
action, transaction_id, connection_id = struct.unpack(">iiQ", reply)
fmt = "connect reply:\n\taction:{}, transaction_id:{}, connection_id:{}"
print(fmt.format(action, transaction_id, connection_id))
return connection_id
def announce(connection_id, dst_ip, dst_port):
request = UdpAnnouceRequest()
request.connection_id = connection_id
request.action = 1
request.transaction_id = 1234 # random
request.info_hash = bytearray.fromhex('1aa4c13830b822c1375686d685a9fce23405f6ba') # compute from meta_info
request.peer_id = bytearray.fromhex('ab' * 20) # random
request.downloaded = 0
request.left = 1509949440 # from meta_info
request.uploaded = 0
request.ip = 0 # default
request.key = 314
request.port = 59601 #
# print(request)
udp_announcing(request, dst_ip, dst_port)
return udp_recv(SOURCE_IP, SOURCE_PORT)
def udp_process():
ip = 'exodus.desync.com' # get from tracker list
port = 6969 # get from tracker list
connection_id = connect(ip, port)
reply = announce(connection_id, ip, port)
action, transaction_id, interval, leechers, seeders = struct.unpack(">iiiii", reply[0:20])
fmt = "announce_reply:\n\taction:{}, transaction_id:{}, interval:{}s, leechers:{}, seeders:{}"
print(fmt.format(action, transaction_id, interval, leechers, seeders))
peers = []
for i in range(20, len(reply), 6):
if i + 6 <= len(reply):
data = reply[i:i + 6]
n1, n2, n3, n4, peer_port = struct.unpack(">BBBBH", data)
peer_ip = "{}.{}.{}.{}".format(n1, n2, n3, n4)
peers.append((peer_ip, peer_port))
print("get {} peers:{}".format(len(peers), peers))
if __name__ == '__main__':
udp_process()
-Process finished with exit code 0
-
-可以看到,确实是110个1。
+运行结果
运行日志如下,可以看到拿到了200个peers的ipv4地址和端口。
+/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/udp_announce.py
received message: b'\x00\x00\x00\x00\x00\x00\x00{\xd89\xa8\x9dv\xd3=]'
connect reply:
action:0, transaction_id:123, connection_id:15580669780121828701
received message: b'\x00\x00\x00\x01\x00\x00\x04\xd2\x00\x00\x07_\x00\x00\x00%\x00\x00\x01qi\xe8\xff\xd8XWl\x18-F\xbcd\x9e\xae}\xea\xaa[\xd41S\'\xcb\x0eb\x14\x8f\xcfbcg\xcd\xaf9\xe0\x1f-\xf7*\xa6\xa2\x86)P\xadl\xbc\xbf)\xb6{\xc4w\xaa)\xcd\xf0@cF--;M\xa6Wp\xce\xc7\xff\xfc\xcf\xbb\x9e\x18\x9c\xec\x96\xc4\xcf\x83PS\x06_\xb4zmc\xd3\x82i\xef\x0b\xeb\xd0\xaf ~d\xf3\x8c\xde\x9b?\xfc\xdb!1\x93W\x0b\xc3I\xad\xf4,)\xf0\x1c\xc5\xe0\x98\x80\xbbZ\xb4^UA#\'|\x95\x7fd\xc77p\xc8Z\xadN\xb5p\xc7K\xbc\xd31i\x9c\xe0\x7f0\xaeV\x02\xc2\xe3G.R\x92\xfc\x15\xc8\x05R\x18\x18I\x943H3~\r\xd3\x89=\x06\xe6\xbb86-y\xd1\xa6\xca\xcb)\xd7\r\x8aLo)\xcb\xda\x8e;K)Z<\x9c\xb0\x87);Q\xd0\xb2\x8b\x18\xa5\xc2T\xb8\xd7\x05\x90;\x83\xf1^\xca\x8eeB\xa1a\xb9\xe8\x15z\x83\xde\x93\n\xba\x91+\x9em\xb1\x9b\xd0V\x82Y\xf2\xb5\xad\xc7\rV\x06\xf8^k\x14R\x8eP\xb1\xfb\x15Rf\x1eV\xd0\x05R-\xd9\xf5\xc2"@\x86\xf0K\x00\x00@B\xdeFa*\x1f;\x02<\xa2\xa7\x18l\xed\xfa\xd2\xf4\x05k#\x81\xe8\xa5\x01\xban\x82\xcd\t\xd57\xb8\x8eQ\xdd\xd4\x1b\x13\xfalh\xd0\x83\xa5J\xa9\xb5\xce\xe7x:\xb8\xe1\xc9\xab\xc6\xf9\xa3:\xc5\xed\xc5\xe4\xd2c\xb6\xb9\x1cL\xc0\x19\x8b<\xbfn\x9a\xc2\x80\xcc\xf3\xc3?\xefn\x8dg\xa5=\x0cm\xb1^\xfcU\xf3[}\xdd\x90\x82yS\xe9o\xf3\xf0R@w\xc0\xf4\'\xae\xd0SR\xcc\xde@\xc5\xf8\xdb\x9e\x8a\x0f\xb3\x08_\x90\xb4\xc1\xa9=\xda$\x1a\xe1n\xe8V\r\xe5\xe1b\x8cr{1\xc3^\x05\xe8bR\x00Sn\xe5\x7fe\xe5G4V5I \x1bjQ\x9a\x80\x7f\xce\xe7y\xd5\xde]\xbc\x81D\xbd\xbf\xf1\xb8K\xdfZ\x8d\xe5\xa9=\xda#\x1a\xe1sE\xbb\xb8\xd4Oi\xa7\x10V\xe9\xffgG\x10\xf3\x8f\xde^\xea#S+%\\\xeeN_\xba\xbd3\x07g\xaf\xdb\xb3)\xa1:#\xe0G%\x98\xce\x84}\xe2\xd8ch\xeaN\xb2\xbe\xabp\xe3\xd2?\xb7\r\xcd\x9d\xf3\x8c\xa8\x01\x18I\xabH\x90\x84,A\x9a\x02}\xd1\\\xdd\x94bp\x8d:\xa7e:p\n\x17\x93\xb0\xb9h\x05\xa9)b\xdc\\\xed\x0f;]\x9dX\x10\x96\x98u\x92R\x18\x8a+\xc2LR\x0cH\xa7z\x06>\x96OI2\x9f.\xb6\xbax9\xee)\xbaS\x8b\x9b\x81)\x8b\xfa&m,)P\xe4\x95\xc8s%\x8e\x03+>\x1e\xc5\xf8\x92\xba\xb9\xca\xc4\xca\xa9\xba\xd3(\xc3Yxr\x07_\xbe:\x08\x08\xc8\x9c\xb2P\x14X\x7f\xcf\x9aIWU\xc6G\x80j\xa0\x9dRSx\xf4n\x07w\xe4m\xbe\xd4`\xc8\xd5g\xf4\xf2}\xe1\x01g5I%\xc1\xa1\\c@\x89\x99\x9bVSE\x95\xfb\xdeV\x12\xe0F\'gR\x17n\x90\x91\xf4O\xb7c\xa6\xd1\xfcOC9\xf0s\xee+\xf6\xc8u\xe1\xed+\xf1\x91DGD)Zo\x9e\xf3y\x18\xcb\x97\x9e=\'\xc4\xca\xa9\xba\xcbO\xa5\xe7(\x0e8\xc1\xa2\xf6k\x92\x14\x82q\x14b.\xe7oi\xe8X\x86\xe2\xbagF\xcc\xc2\xed\x83`+\xbdk\x99\xdfR\x07u\xa2\xbf\x8cBW\xcfc\x00\x00?\x8f\xecq\x00\x00)\xd2\n\xe3\xe9\xca\x1f-g\x8a\xe5y\xde\xb8\xe8\xad\xab\xc4\xd8\x97\xb8\x9f\xd0d\xd0g\xe9\xa2\xdb\xbd\xc4\xd1\xe9\xa4\x86\xc2\xc4\xcf\xb83\xe2\xaa\xc3\x93\xb5\xbe&\xfb\x9c\xde.\xdc\xd2\x14\x9aF9B\xa4{p\xcd\x9d\xf6\xe0\xcai\xb8\x99u\xf0+g{$\xb5\xb3LZ\xdbs\xab\x99~Qd\xff\xdf\xe8\xefP\'\x86\xc9:\x8aMD\xf3F\x88\xb8H\xe5%G\x84M1\x92\r~\xe9\x13)OB\xe1Vi\xdd\xcb\x16\xf53\xb3\xba\x96\x86\xaf\xba:\xb2\xa6!$\x7f\xdd|k\xc0+\x8a}{\xf5\x14\x8eAVy68&K\xb1tZ`\nY\xb8l\x04\xd7\xfb\x84\xb3h\xcfSb*\x93`\x17\xb5[\xebGV\x89\x15S\x91\xd1KA\xd7\xc4\xa3o?\xf5:\x9b\x83\x1d.\xd1,c\x05\x16\xb6\xba\xfd\xab97\xb2\x9d\xf9>g\xeb\x82i\xa4{G\xb7i\xe8V1\x91\xafg\xfc\xca\x96\xaf\x16g\x04\xf8\x8a\xda!_^\x9d\xaf\xd2-^\xaf\x94\x0c\xbc\x0f^L\x0f,\xc3\x00Y\xedbW\xf5\x84WFe\xc7\xaeAV\x19|\x9e\xb0\xcfH\xe6\xda\x88<\xdcFA\xe3\x15q\x08)\x8b\xf3:L\x1b)<\xe9\xf5\xaa[\x1f\xd7\x0e\x08Z\xb2\x01\t\x87,R\xd9\xcb\xd4\x9bSx"\xca\x8e[f`N\xc5\xed\x81\x96b\xdc\xc5\xe8\x1av#\'\xc5\xe8\x13n\xdbn\xc4\xf9g\xc4:\x15\xbbp|\xa6#\'\xb7W\xa9\x9b@5u\xc2\xdcQd\xef'
announce_reply:
action:1, transaction_id:1234, interval:1887s, leechers:37, seeders:369
get 200 peers:[('105.232.255.216', 22615), ('108.24.45.70', 48228), ('158.174.125.234', 43611), ('212.49.83.39', 51982), ('98.20.143.207', 25187), ('103.205.175.57', 57375), ('45.247.42.166', 41606), ('41.80.173.108', 48319), ('41.182.123.196', 30634), ('41.205.240.64', 25414), ('45.45.59.77', 42583), ('112.206.199.255', 64719), ('187.158.24.156', 60566), ('196.207.131.80', 21254), ('95.180.122.109', 25555), ('130.105.239.11', 60368), ('175.32.126.100', 62348), ('222.155.63.252', 56097), ('49.147.87.11', 49993), ('173.244.44.41', 61468), ('197.224.152.128', 47962), ('180.94.85.65', 8999), ('124.149.127.100', 50999), ('112.200.90.173', 20149), ('112.199.75.188', 54065), ('105.156.224.127', 12462), ('86.2.194.227', 18222), ('82.146.252.21', 51205), ('82.24.24.73', 37939), ('72.51.126.13', 54153), ('61.6.230.187', 14390), ('45.121.209.166', 51915), ('41.215.13.138', 19567), ('41.203.218.142', 15179), ('41.90.60.156', 45191), ('41.59.81.208', 45707), ('24.165.194.84', 47319), ('5.144.59.131', 61790), ('202.142.101.66', 41313), ('185.232.21.122', 33758), ('147.10.186.145', 11166), ('109.177.155.208', 22146), ('89.242.181.173', 50957), ('86.6.248.94', 27412), ('82.142.80.177', 64277), ('82.102.30.86', 53253), ('82.45.217.245', 49698), ('64.134.240.75', 0), ('64.66.222.70', 24874), ('31.59.2.60', 41639), ('24.108.237.250', 54004), ('5.107.35.129', 59557), ('1.186.110.130', 52489), ('213.55.184.142', 20957), ('212.27.19.250', 27752), ('208.131.165.74', 43445), ('206.231.120.58', 47329), ('201.171.198.249', 41786), ('197.237.197.228', 53859), ('182.185.28.76', 49177), ('139.60.191.110', 39618), ('128.204.243.195', 16367), ('110.141.103.165', 15628), ('109.177.94.252', 22003), ('91.125.221.144', 33401), ('83.233.111.243', 61522), ('64.119.192.244', 10158), ('208.83.82.204', 56896), ('197.248.219.158', 35343), ('179.8.95.144', 46273), ('169.61.218.36', 6881), ('110.232.86.13', 58849), ('98.140.114.123', 12739), ('94.5.232.98', 20992), ('83.110.229.127', 26085), ('71.52.86.53', 18720), ('27.106.81.154', 32895), ('206.231.121.213', 56925), ('188.129.68.189', 49137), ('184.75.223.90', 36325), ('169.61.218.35', 6881), ('115.69.187.184', 54351), ('105.167.16.86', 59903), ('103.71.16.243', 36830), ('94.234.35.83', 11045), ('92.238.78.95', 47805), ('51.7.103.175', 56243), ('41.161.58.35', 57415), ('37.152.206.132', 32226), ('216.99.104.234', 20146), ('190.171.112.227', 53823), ('183.13.205.157', 62348), ('168.1.24.73', 43848), ('144.132.44.65', 39426), ('125.209.92.221', 37986), ('112.141.58.167', 25914), ('112.10.23.147', 45241), ('104.5.169.41', 25308), ('92.237.15.59', 23965), ('88.16.150.152', 30098), ('82.24.138.43', 49740), ('82.12.72.167', 31238), ('62.150.79.73', 12959), ('46.182.186.120', 14830), ('41.186.83.139', 39809), ('41.139.250.38', 27948), ('41.80.228.149', 51315), ('37.142.3.43', 15902), ('197.248.146.186', 47562), ('196.202.169.186', 54056), ('195.89.120.114', 1887), ('190.58.8.8', 51356), ('178.80.20.88', 32719), ('154.73.87.85', 50759), ('128.106.160.157', 21075), ('120.244.110.7', 30692), ('109.190.212.96', 51413), ('103.244.242.125', 57601), ('103.53.73.37', 49569), ('92.99.64.137', 39323), ('86.83.69.149', 64478), ('86.18.224.70', 10087), ('82.23.110.144', 37364), ('79.183.99.166', 53756), ('79.67.57.240', 29678), ('43.246.200.117', 57837), ('43.241.145.68', 18244), ('41.90.111.158', 62329), ('24.203.151.158', 15655), ('196.202.169.186', 52047), ('165.231.40.14', 14529), ('162.246.107.146', 5250), ('113.20.98.46', 59247), ('105.232.88.134', 58042), ('103.70.204.194', 60803), ('96.43.189.107', 39391), ('82.7.117.162', 49036), ('66.87.207.99', 0), ('63.143.236.113', 0), ('41.210.10.227', 59850), ('31.45.103.138', 58745), ('222.184.232.173', 43972), ('216.151.184.159', 53348), ('208.103.233.162', 56253), ('196.209.233.164', 34498), ('196.207.184.51', 58026), ('195.147.181.190', 9979), ('156.222.46.220', 53780), ('154.70.57.66', 42107), ('112.205.157.246', 57546), ('105.184.153.117', 61483), ('103.123.36.181', 45900), ('90.219.115.171', 39294), ('81.100.255.223', 59631), ('80.39.134.201', 14986), ('77.68.243.70', 35000), ('72.229.37.71', 33869), ('49.146.13.126', 59667), ('41.79.66.225', 22121), ('221.203.22.245', 13235), ('186.150.134.175', 47674), ('178.166.33.36', 32733), ('124.107.192.43', 35453), ('123.245.20.142', 16726), ('121.54.56.38', 19377), ('116.90.96.10', 22968), ('108.4.215.251', 33971), ('104.207.83.98', 10899), ('96.23.181.91', 60231), ('86.137.21.83', 37329), ('75.65.215.196', 41839), ('63.245.58.155', 33565), ('46.209.44.99', 1302), ('182.186.253.171', 14647), ('178.157.249.62', 26603), ('130.105.164.123', 18359), ('105.232.86.49', 37295), ('103.252.202.150', 44822), ('103.4.248.138', 55841), ('95.94.157.175', 53805), ('94.175.148.12', 48143), ('94.76.15.44', 49920), ('89.237.98.87', 62852), ('87.70.101.199', 44609), ('86.25.124.158', 45263), ('72.230.218.136', 15580), ('70.65.227.21', 28936), ('41.139.243.58', 19483), ('41.60.233.245', 43611), ('31.215.14.8', 23218), ('1.9.135.44', 21209), ('203.212.155.83', 30754), ('202.142.91.102', 24654), ('197.237.129.150', 25308), ('197.232.26.118', 8999), ('197.232.19.110', 56174), ('196.249.103.196', 14869), ('187.112.124.166', 8999), ('183.87.169.155', 16437), ('117.194.220.81', 25839)]
Process finished with exit code 0
]]>
BEP3的peer message章节说明了几种消息类型。 +其中bitfield消息理解起来是容易的,但是实际过程中却略有不同。
+ + +++‘bitfield’ is only ever sent as the first message. Its payload is a bitfield with each index that downloader has sent set to one and the rest set to zero. Downloaders which don’t have anything yet may skip the ‘bitfield’ message. The first byte of the bitfield corresponds to indices 0 - 7 from high bit to low bit, respectively. The next one 8-15, etc. Spare bits at the end are set to zero.
+
这段话的意思很简单,举例说明一下,如果一个文件有10个分片,那么如果没有下载任何数据,那么bitfield的值应该是(为了便于阅读每个字节的二进制码 以一个空格键隔开)
+0b0000 0000 0000
+
+如果下载了piece 0, 则值应该是
+0b1000 0000 0000
+
+如果全部下载完成,则应该是
+0b1111 1111 1100
+
+一共是10个1,表示10个piece。
+但是实际上呢,对于一个下载完成的文件,获取到的bitfield值是0b111111111100
么?
我们可以自己制作一个种子来验证一下。
+用uTorrent软件选择本地的一个文件制作一个种子文件。如下图所示
+ +该文件有110个片。
+我们读取代码如下
+
|
我们看到日志如下
+/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
+sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
+Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xab\x9c\xa8$G\x8euX\x91\xad\x9e')
+id=20
+recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
+id=5
+recv=b'\xff\xff\xffg~\xaf\xbf=\x7f\xff\xeb&\xdd\x98'
+1111111111111111111111110110011101111110101011111011111100111101011111111111111111101011001001101101110110011000
+
+这是怎么回事,为什么中间会有这么多0呢,不是已经下载完成了么?
+111111111111111111111111011001110111111010101111101111110011110...
+
+应该是110个1才对。而且如果再运行几遍会发现每次有不同的index的片是0。
+原来这是uTorrent软件实现的一个策略,即使已经下载完成,也不会返回110个1的bitfield。而是用have消息
来
+补全,也就是id为4的peer消息。have消息的值是一个piece的index值,表示该piece下载完成。
因此,我们需要根据bitfield消息和have消息来看是否下载完成。代码修改一下,每接收到一个have消息就把相应的index +置为1。
+
|
我们再看一下日志
+/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
+sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
+Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xabY\x08\r\x82=\xd1J^\x930')
+id=20
+recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
+id=5
+recv=b'\xfb\xdf\xbf\xee\xdf\xdf\x9d\xef\xb9\xfa\xfbv^\xf8'
+1111101111011111101111111110111011011111110111111001110111101111101110011111101011111011011101100101111011111000
+id=4
+recv=b'\x00\x00\x002'
+index=50
+1111101111011111101111111110111011011111110111111011110111101111101110011111101011111011011101100101111011111000
+
+...
+...
+...
+
+id=4
+recv=b'\x00\x00\x00b'
+index=98
+1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111100
+id=4
+recv=b'\x00\x00\x00X'
+index=88
+1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
+id=4
+recv=b'\x00\x00\x00\n'
+index=10
+1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
+already recv:b''
+timed out
+Traceback (most recent call last):
+ File "/Users/ym/charm/pytest/ym/bt/peer_protocol.py", line 40, in recv
+ tmp = sock.recv(n - len(data))
+socket.timeout: timed out
+
+Process finished with exit code 0
+
+可以看到,确实是110个1。
]]>UTF-8
。
最近基于tsar(阿里开源的一个基于c语言的监控程序)做二次开发, -因为以前从来没有在工作中写过c,所以这个简单的工作花了两周时间,期间用gdb进行调试,用valgrind检查内存泄漏。 -但是最让我不舒服的就是gdb调试了,虽然gdb很给力,但是毕竟由奢入俭难,之前写Java,Python,Go都是可以用IDE进行 -debug的。有图形化界面还是效率高很多,而对于新手,能够方便的debug源码就可以快速的理解项目。
-那么怎么办呢?
- - - -我一直以为CLion可以很好的理解cmake项目,但是对于大量的基于makefile编译的项目,则不能很好的解析,所以也无法利用CLion -进行代码调试。
-但是并不是这样,虽然CLion无法理解代码中各种符号之间的依赖关系,但是调试只要有debug info和源码就可以进行图形化调试。
-参考CLion远程开发的Remote GDB Server,很简单就实现了。
-mac下安装了CLion,linux下编译tsar。
-在mac下,tsar项目源码路径——/Users/ym/work/operation
-在linux下为,tsar项目源码路径——/home/keyvalue/ym/operation
在CLion下创建一个GDB remote debug
配置。
-参数配置:
target remote args
10.4.104.153:1234 # 这是gdbserver的ip:port
path mappings
-remote path为/home/keyvalue/ym/operation
,local path为/Users/ym/work/operation
在linux下运行命令
-
|
CLion下启动之前的GDB remote debug
配置,就可以愉快的断点调试了。
/home/keyvalue/ym/operation
,local path为/Use
]]>
- certificate
- X.509
- nginx
- openssl
- ssl
+ certificate
+ X.509
+ nginx
+ openssl
+ ssl
+
+
最近基于tsar(阿里开源的一个基于c语言的监控程序)做二次开发, +因为以前从来没有在工作中写过c,所以这个简单的工作花了两周时间,期间用gdb进行调试,用valgrind检查内存泄漏。 +但是最让我不舒服的就是gdb调试了,虽然gdb很给力,但是毕竟由奢入俭难,之前写Java,Python,Go都是可以用IDE进行 +debug的。有图形化界面还是效率高很多,而对于新手,能够方便的debug源码就可以快速的理解项目。
+那么怎么办呢?
+ + + +我一直以为CLion可以很好的理解cmake项目,但是对于大量的基于makefile编译的项目,则不能很好的解析,所以也无法利用CLion +进行代码调试。
+但是并不是这样,虽然CLion无法理解代码中各种符号之间的依赖关系,但是调试只要有debug info和源码就可以进行图形化调试。
+参考CLion远程开发的Remote GDB Server,很简单就实现了。
+mac下安装了CLion,linux下编译tsar。
+在mac下,tsar项目源码路径——/Users/ym/work/operation
+在linux下为,tsar项目源码路径——/home/keyvalue/ym/operation
在CLion下创建一个GDB remote debug
配置。
+参数配置:
target remote args
10.4.104.153:1234 # 这是gdbserver的ip:port
path mappings
+remote path为/home/keyvalue/ym/operation
,local path为/Users/ym/work/operation
在linux下运行命令
+
|
CLion下启动之前的GDB remote debug
配置,就可以愉快的断点调试了。
CLion
是一款针对C/C++项目的跨平台的集成IDE(A cross-platform IDE for C and C++)。2020版本以前,只支持cmake项目,
+但是2021版本对Makefile项目的支持度增加了。我们看看如何对Makefile项目进行断点调试。
我们用一个实际的项目作为例子。
+Go语言项目从源码编译有几种方式,其中一种方式是基于Bootstrap toolchain from C source code,
+也就是说,首先编译Go 1.4版本,然后用编译出来的Go
,编译最新的Go版本。(UPDATE: Mac12.1 Monterey系统上不支持)
那么我下载这个Go1.4版本,go1.4-bootstrap-20171003.tar.gz, +解压到某个路径下。
+
|
进入src路径下,运行make.bash文件,则开始编译。(当然需要build相关的工具)。为了看清楚bash脚本执行的内容,我们修改make.bash的
+第一行改为 set -ex
, 这样会打印详细的执行内容如下,
|
我们看到15-33行, 首先编译了cmd/dist
文件夹下的c代码,生成了一个dist可执行文件,并调用了 /cmd/dist/dist env -p
+# Building C bootstrap tool. ++ echo cmd/dist +cmd/dist +++ cd .. +++ pwd ++ export GOROOT=/Users/ym/work/go1.4 ++ GOROOT=/Users/ym/work/go1.4 ++ GOROOT_FINAL=/Users/ym/work/go1.4 ++ DEFGOROOT='-DGOROOT_FINAL="/Users/ym/work/go1.4"' ++ mflag= ++ case "$GOHOSTARCH" in +++ uname ++ '[' Darwin == Darwin ']' ++ mflag=' -mmacosx-version-min=10.6' +++ type -t gcc +++ type -t clang ++ '[' -z '' -a -z file -a -n file ']' ++ gcc -mmacosx-version-min=10.6 -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/Users/ym/work/go1.4"' cmd/dist/arm.c cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/plan9.c cmd/dist/unix.c cmd/dist/windows.c +++ ./cmd/dist/dist env -p ++ +
那么我们来看看怎么在CLion中调试/cmd/dist/dist env -p
如果要用CLion直接打开cmd/dist文件夹,会提示创建cmake项目,但是我们想直接使用调试Makefile项目。 +那么我们在cmd/dist目录下创建一个Makefile文件,内容如下
+
|
其实就是根据make.bash打印的gcc编译命令改写的。需要注意的是要增加-g
选项,这样才能调试。
然后参考Run/Debug Configuration: Makefile Application
+配置几个参数,核心要指定的就是Target和Executable文件 +
+最后先clean,然后断点调试unix.c
文件中的main入口函数。
成功了。
+]]>CLion
是一款针对C/C++项目的跨平台的集成IDE(A cross-platform IDE for C and C++)。2020版本以前,只支持cmake项目,
-但是2021版本对Makefile项目的支持度增加了。我们看看如何对Makefile项目进行断点调试。
我们用一个实际的项目作为例子。
-Go语言项目从源码编译有几种方式,其中一种方式是基于Bootstrap toolchain from C source code,
-也就是说,首先编译Go 1.4版本,然后用编译出来的Go
,编译最新的Go版本。(UPDATE: Mac12.1 Monterey系统上不支持)
那么我下载这个Go1.4版本,go1.4-bootstrap-20171003.tar.gz, -解压到某个路径下。
-
|
进入src路径下,运行make.bash文件,则开始编译。(当然需要build相关的工具)。为了看清楚bash脚本执行的内容,我们修改make.bash的
-第一行改为 set -ex
, 这样会打印详细的执行内容如下,
|
我们看到15-33行, 首先编译了cmd/dist
文件夹下的c代码,生成了一个dist可执行文件,并调用了 /cmd/dist/dist env -p
-# Building C bootstrap tool. -+ echo cmd/dist -cmd/dist -++ cd .. -++ pwd -+ export GOROOT=/Users/ym/work/go1.4 -+ GOROOT=/Users/ym/work/go1.4 -+ GOROOT_FINAL=/Users/ym/work/go1.4 -+ DEFGOROOT='-DGOROOT_FINAL="/Users/ym/work/go1.4"' -+ mflag= -+ case "$GOHOSTARCH" in -++ uname -+ '[' Darwin == Darwin ']' -+ mflag=' -mmacosx-version-min=10.6' -++ type -t gcc -++ type -t clang -+ '[' -z '' -a -z file -a -n file ']' -+ gcc -mmacosx-version-min=10.6 -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/Users/ym/work/go1.4"' cmd/dist/arm.c cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/plan9.c cmd/dist/unix.c cmd/dist/windows.c -++ ./cmd/dist/dist env -p -- -
那么我们来看看怎么在CLion中调试/cmd/dist/dist env -p
如果要用CLion直接打开cmd/dist文件夹,会提示创建cmake项目,但是我们想直接使用调试Makefile项目。 -那么我们在cmd/dist目录下创建一个Makefile文件,内容如下
-
|
其实就是根据make.bash打印的gcc编译命令改写的。需要注意的是要增加-g
选项,这样才能调试。
然后参考Run/Debug Configuration: Makefile Application
-配置几个参数,核心要指定的就是Target和Executable文件 -
-最后先clean,然后断点调试unix.c
文件中的main入口函数。
成功了。
-]]>MacOS下,LLDB调试Qt5应用通常会遇到无法打印QString变量的问题。
-
|
这里无法获取变量s
的值Hello World
,只能看到一个地址。
同样的,使用CLion等IDE使用LLDB调试Qt5应用的时候,也无法看到值。
-英雄来了, https://github.com/ayuckhulk/lldb-qt-formatters
---I use Xcode and LLDB to debug my Qt programs, and got tired with there being no visualisation for all the built-in types. Here I endeavour to make all of these types visible through the debugger. Works with Qt 5.x. Tested with Qt 5.9.8, 5.13.2 and XCode 11.
-
作者使用Xcode和LLDB开发Qt,苦于debug的时候看不到qt的内建类型的值。
-怎么办呢,简单的说就是LLDB启动的时候,加载python脚本,把内建类型的显示值的计算方法替换掉。也就是LLDB可以加载这样的自定义Formatter, -对变量进行格式化输出 —— Variable Formatting, -Python脚本非常适合实现复杂的Formatter —— Python Scripting
-安装方法如下:
---git clone this repo somewhere, e.g. ~/qtlldb. Then add the following lines to your ~/.lldbinit:
-command script import ~/qtlldb/QtFormatters.py -command source ~/qtlldb/QtFormatters.lldb
-
这样,Qt的如下内建类型的值都可以在调试的时候可视化了。
-还用如下test.cpp
代码测试,仅仅测试QString
类型。
|
编译一下
-
|
这里需要设置PKG_CONFIG_PATH
一下
|
然后LLDB调试a.out。
-
|
这里18行”Hello World”显示出来了。
-对LLDB启动进行设置之后,CLion里也可以愉快的调试了。
- -]]>MacOS下,LLDB调试Qt5应用通常会遇到无法打印QString变量的问题。
+
|
这里无法获取变量s
的值Hello World
,只能看到一个地址。
同样的,使用CLion等IDE使用LLDB调试Qt5应用的时候,也无法看到值。
+英雄来了, https://github.com/ayuckhulk/lldb-qt-formatters
+++I use Xcode and LLDB to debug my Qt programs, and got tired with there being no visualisation for all the built-in types. Here I endeavour to make all of these types visible through the debugger. Works with Qt 5.x. Tested with Qt 5.9.8, 5.13.2 and XCode 11.
+
作者使用Xcode和LLDB开发Qt,苦于debug的时候看不到qt的内建类型的值。
+怎么办呢,简单的说就是LLDB启动的时候,加载python脚本,把内建类型的显示值的计算方法替换掉。也就是LLDB可以加载这样的自定义Formatter, +对变量进行格式化输出 —— Variable Formatting, +Python脚本非常适合实现复杂的Formatter —— Python Scripting
+安装方法如下:
+++git clone this repo somewhere, e.g. ~/qtlldb. Then add the following lines to your ~/.lldbinit:
+command script import ~/qtlldb/QtFormatters.py +command source ~/qtlldb/QtFormatters.lldb
+
这样,Qt的如下内建类型的值都可以在调试的时候可视化了。
+还用如下test.cpp
代码测试,仅仅测试QString
类型。
|
编译一下
+
|
这里需要设置PKG_CONFIG_PATH
一下
|
然后LLDB调试a.out。
+
|
这里18行”Hello World”显示出来了。
+对LLDB启动进行设置之后,CLion里也可以愉快的调试了。
+ +]]>