Nutz的 数据库事务

Posted on

Nutz的 数据库事务

1.数据库事务 数据库的事务是作为单个逻辑工作单元执行的一系列操作。一个逻辑工作单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务。一般来讲,隔离性是通过锁机制来实现,而其他三个特性通过日志机制实现。 当一个数据库的某些数据由多个客户端并发访问操作的时候,可能会出现一系列的问题:(1)脏读(dirty read),(2)不可重复读(unrepeatable read),(3)幻象读(phantom read),(4)丢失更新

2.使用Nutz来处理事务 在nutz中使用一个类org.nutz.trans.Trans来处理事务,使用方式如下: Java代码 收藏代码

  1. //TODO 选择事务级别
  2. int level=Connection.TRANSACTION_SERIALIZABLE;
  3. Trans.exec(level, new Atom(){
  4. @Override
  5. public void run() {
  6. //TODO 事务内的操作逻辑
  7. }
  8. });

当然你也可是使用如下默认方式:

Java代码 收藏代码

  1. Trans.exec(new Atom(){
  2. @Override
  3. public void run() {
  4. //TODO
  5. }
  6. });

    在这种情况下,level默认是Connection.TRANSACTION_READ_COMMITTED 注:如果你使用jdbc来直接操作事务,当然你可以在某一个位置设置保存点,如果事务失败,可以回滚到那个点,在nutz中整个事务要么成功,要么失败,全部回滚。如果你希望使用savepoint改怎么办呢,你可以这么用:

Java代码 收藏代码

  1. Dao dao=null;
  2. dao.run(new ConnCallback(){
  3. @Override
  4. public void invoke(Connection conn) throws Exception {
  5. // TODO 在这里面使用Connection来操作
  6. }
  7. });

3.ThreadLocal Nutz在实现Trans这个类的时候用到了ThreadLcoal(方便了使用者存储变量到当前线程。),用来存储用户的每一次事务动作。具体实现请参见源代码类org.nutz.trans.Trans。

来源: [http://amosleaf.iteye.com/blog/548337](http://amosleaf.iteye.com/blog/548337)

4、事务的嵌套

设置事务的级别

在 JDBC 的 java.sql.Connection 接口中定义的 setTransactionIsolation 函数可以设置事务的级别Nutz.Dao 也提供另外一个静态函数,允许你设置事务的级别:

Trans.exec 的函数声明 public static void exec(int level, Atom... atoms);

这里的第一个参数 level 和 java.sql.Connection 接口中的 setTransactionIsolation 规定的 level 是一样的。下面是在 java.sql.Connection 里面关于 level 参数的 JDoc 说明:

它可以是下列常量中的任意一个值:

  • Connection.TRANSACTION_READ_UNCOMMITTED
  • Connection.TRANSACTION_READ_COMMITTED
  • Connection.TRANSACTION_REPEATABLE_READ
  • Connection.TRANSACTION_SERIALIZABLE

注意: 不能使用常量 Connection.TRANSACTION_NONE,因为它的意思是“不支持事务”

关于 level 参数的更多说明,请参看java.sql.Connection 的文档)

不同的数据库,对于 JDBC 事务级别的规范,支持的力度不同。请参看相应数据库的文档,已 确定你设置的数据库事务级别是否被支持。

/#Top

事务的嵌套

Nutz 的事务模板可以嵌套吗? 答案是肯定的。事实上,Nutz 支持事务模板的无限层级嵌套。

这里,如果每一层嵌套,指定的事务级别有所不同,不同的数据库,可能引发不可预知的错误。所以,嵌套的事务模板的事务,将以最顶层的事务为级别为标准。就是说,如果最顶层的事务级别为 'TRANSACTION_READ_COMMITTED',那么下面所包含的所有事务,无论你指定什么样的事务级别,都是 'TRANSACTION_READ_COMMITTED', 这一点,由抽象类 Transaction 来保证。其 setLevel 当被设置了一个大于 0 的整数以后,将不再 接受任何其他的值。

你可以通过继承 Transaction 来修改这个默认的行为,当然,这个行为修改一般是没有必要的。

另外,你还可能需要知道,通过 Trans.setup 方法,能让整个虚拟机的 Nutz 事务操作都使用你的 Transaction 实现

下面我给出两个例子:

最外层模板决定了整个事务的级别: Trans.exec(Connection.TRANSACTION_READ_COMMITTED, new Atom(){ public void run(){ dao.update(xxx); dao.update(bbb); // 在下层模板,虽然你指定了新的事务级别,但是这里的事务级别还是 // 'TRANSACTION_READ_COMMITTED'。在一个事务中,级别一旦设定就不可更改 Trans.exec(Connection.TRANSACTION_SERIALIZABLE, new Atom(){ public void run(){ dao.update(CCC); dao.update(EEE); } }); } });

让整个函数都是事务的:

public void updatePet(final Pet pet){ Trans.exec(new Atom(){ public void run(){ dao.update(pet); dao.update(pet.getMaster()); } }); } // 在另外一个函数里,可以这么使用 public void updateDogAndCat(final Pet dog, final Pet cat){ Trans.exec(new Atom(){ public void run(){ updatePet(dog); updatePet(cat); } }); }

来源: [http://code.google.com/p/nutz/wiki/dao_transaction](http://code.google.com/p/nutz/wiki/dao_transaction) 以上需要注意的几个细节,文档还没提及: 1. 数据库连接池的AutoCommit属性 对于我见到的大部分连接池和数据库驱动,默认情况下AutoCommit=true,应该是为了性能 这就导致如果用户不自行设置的话,Trans的大部分操作都是不可回滚的.故,如果使用Trans,那么务必设置AutoCommit

2. 多个数据源的事务 大部分情况下,用户都不是使用XA驱动,故,多数据源的事务提交,在最后的提交阶段出错的话,已经执行commit操作的数据源,是不可能回滚的

3. 子线程的事务 考虑下面的代码: Trans.exec(new Atom(){ public void run() { dao.insert(pet); new MySuperPowerThread().start(); dao.count(Pet.class); }});

那么MySuperPowerThread的代码并不是在事务模板中执行的,因为这里的事务不会被子线程继承

4. 返回值问题(更新了) 使用Molecule Molecule mc = new Molecule() { public void run() { dao.insert(pet); setObj(dao.count(Pet.class)); }};Trans.exec(mc);System.out.println(mc.getObj());

来源: [http://wendal.net/324.html](http://wendal.net/324.html)

InterpolationSearch

Posted on

InterpolationSearch

Algorithm Gossip: 插补搜寻法

说明

如果却搜寻的资料分布平均的话,可以使用插补(Interpolation)搜寻法来进行搜寻,在搜寻的对象大于500时,插补搜寻法会比 二分搜寻法 来的快速。

解法

插补搜寻法是以资料分布的近似直线来作比例运算,以求出中间的索引并进行资料比对,如果取出的值小于要寻找的值,则提高下界,如果取出的值大于要寻找的 值,则降低下界,如此不断的减少搜寻的范围,所以其本原则与二分搜寻法是相同的,至于中间值的寻找是透过比例运算,如下所示,其中K是指定要寻找的对象, 而m则是可能的索引值: 插补搜寻

实作

  • C /#include /#include /#include /#define MAX 10 /#define SWAP(x,y) {int t; t = x; x = y; y = t;} void quicksort(int[], int, int); int intsrch(int[], int); int main(void) { int number[MAX] = {0}; int i, find; srand(time(NULL)); for(i = 0; i < MAX; i++) { number[i] = rand() % 100; } quicksort(number, 0, MAX-1); printf("数列:"); for(i = 0; i < MAX; i++) printf("%d ", number[i]); printf("\n输入寻找对象:"); scanf("%d", &find); if((i = intsrch(number, find)) >= 0) printf("找到数字于索引 %d ", i); else printf("\n找不到指定数"); printf("\n"); return 0; } int intsrch(int number[], int find) { int low, mid, upper; low = 0; upper = MAX - 1; while(low <= upper) { mid = (upper-low)/* (find-number[low])/(number[upper]-number[low])
  • low; if(mid < low || mid > upper) return -1; if(find < number[mid]) upper = mid - 1; else if(find > number[mid]) low = mid + 1; else return mid; } return -1; } void quicksort(int number[], int left, int right) { int i, j, k, s; if(left < right) { s = number[(left+right)/2]; i = left - 1; j = right + 1; while(1) { while(number[++i] < s) ; // 向右找 while(number[--j] > s) ; // 向左找 if(i >= j) break; SWAP(number[i], number[j]); } quicksort(number, left, i-1); // 对左边进行递回 quicksort(number, j+1, right); // 对右边进行递回 } }
  • Java public class InterpolationSearch { public static int search(int[] number, int des) { int low = 0; int upper = number.length - 1; while(low <= upper) { int mid = (upper-low)/* (des-number[low])/(number[upper]-number[low])
  • low; if(mid < low || mid > upper) return -1; if(des < number[mid]) upper = mid - 1; else if(des > number[mid]) low = mid + 1; else return mid; } return -1; } public static void main(String[] args) { int[] number = {1, 4, 2, 6, 7, 3, 9, 8}; QuickSort.sort(number); int find = InterpolationSearch.search(number, 3); if(find != -1) System.out.println("找到数值于索引" + find); else System.out.println("找不到数值"); } }

LongPI

Posted on

LongPI

Algorithm Gossip: 长 PI

说明

圆周率后的小数位数是无止境的,如何使用电脑来计算这无止境的小数是一些数学家与程式设计师所感兴趣的,在这边介绍一个公式配合 大数运算,可以计算指定位数的圆周率。

解法

首先介绍J.Marchin的圆周率公式: PI = [16/5 - 16 / (3/53) + 16 / (5/55) - 16 / (7/57) + ......] - [4/239 - 4/(3/2393) + 4/(5/2395) - 4/(7/2397) + ......] 可以将这个公式整理为: PI = [16/5 - 4/239] - [16/(53) - 4/(2393)]/3+ [16/(55) - 4/(2395)]/5 + ...... 也就是说第n项,若为奇数则为正数,为偶数则为负数,而项数表示方式为: [16/52/n-1 - 4/2392/n-1] / (2/n-1) 如果我们要计算圆周率至10的负L次方,由于[16/52/n-1 - 4/2392/n-1]中16/52/n-1比4/2392/n-1来的大,具有决定性,所以表示至少必须计算至第n项: [16/52/n-1 ] / (2/n-1) = 10-L 将上面的等式取log并经过化简,我们可以求得: n = L / (2log5) = L / 1.39794 所以若要求精确度至小数后L位数,则只要求至公式的第n项,其中n等于: n = [L/1.39794] + 1 在上式中[]为高斯符号,也就是取至整数(不大于L/1.39794的整数);为了计简方便,可以在程式中使用下面这个公式来计简第n项: [Wn-1/52- Vn-1 / (2392)] / (2/n-1) 这个公式的演算法配合大数运算函式的演算法为:

div(w, 25, w); div(v, 239, v); div(v, 239, v); sub(w, v, q); div(q, 2/*k-1, q)

至于大数运算的演算法,请参考之前的文章,必须注意的是在输出时,由于是输出阵列中的整数值,如果阵列中整数位数不满四位,则必须补上0,在C语言中只要 使用格式指定字%04d,使得不足位数部份自动补上0再输出,至于Java的部份,使用 NumberFormat来作格式化。 您可以参考 这个网站 有关于另一个用公式求PI的说明,它的实作在 这边

实作

  • C /#include /#define L 1000 /#define N L/4+1 // L 为位数,N是array长度 void add(int/, int/, int/); void sub(int/, int/, int/); void div(int/, int, int/); int main(void) { int s[N+3] = {0}; int w[N+3] = {0}; int v[N+3] = {0}; int q[N+3] = {0}; int n = (int)(L/1.39793 + 1); int k; w[0] = 16/5; v[0] = 4/239; for(k = 1; k <= n; k++) { // 套用公式 div(w, 25, w); div(v, 239, v); div(v, 239, v); sub(w, v, q); div(q, 2/k-1, q); if(k%2) // 奇数项 add(s, q, s); else // 偶数项 sub(s, q, s); } printf("%d.", s[0]); for(k = 1; k < N; k++) printf("%04d", s[k]); printf("\n"); return 0; } void add(int /a, int /b, int /c) { int i, carry = 0; for(i = N+1; i >= 0; i--) { c[i] = a[i] + b[i] + carry; if(c[i] < 10000) carry = 0; else { // 进位 c[i] = c[i] - 10000; carry = 1; } } } void sub(int /a, int /b, int /c) { int i, borrow = 0; for(i = N+1; i >= 0; i--) { c[i] = a[i] - b[i] - borrow; if(c[i] >= 0) borrow = 0; else { // 借位 c[i] = c[i] + 10000; borrow = 1; } } } void div(int /a, int b, int /c) { // b 为除数 int i, tmp, remain = 0; for(i = 0; i <= N+1; i++) { tmp = a[i] + remain; c[i] = tmp / b; remain = (tmp % b) / 10000; } }

  • Java import java.text.NumberFormat; public class LongPI { public static void main(String[] args) { final int L = 1000; final int N = L/4+1; int[] s = new int[N+3]; int[] w = new int[N+3]; int[] v = new int[N+3]; int[] q = new int[N+3]; int n = (int)(L/1.39793 + 1); w[0] = 16/5; v[0] = 4/239; for(int k = 1; k <= n; k++) { // 套用公式 w = BigNumber.div(w, 25); v = BigNumber.div(v, 239); v = BigNumber.div(v, 239); q = BigNumber.sub(w, v); q = BigNumber.div(q, 2/*k-1); if(k % 2 == 1) // 奇数项 s = BigNumber.add(s, q); else // 偶数项 s = BigNumber.sub(s, q); } NumberFormat nf = NumberFormat.getInstance(); nf.setMinimumIntegerDigits(4); System.out.print(s[0] + "."); for(int k = 1; k < N; k++) System.out.print(nf.format(s[k])); System.out.println(); } }

HTTPS是如何保证连接安全:每位Web开发者都应知道的

Posted on

HTTPS是如何保证连接安全:每位Web开发者都应知道的 - 博客 - 伯乐在线

本文由 伯乐在线 - 伯乐在线读者 翻译自 hartley brody。转载请参见文章末尾处的要求。

【感谢@埃姆杰 的热心翻译。如果其他朋友也有不错的原创或译文,可以尝试推荐给伯乐在线。】

“HTTPS协议的工作原理是什么?”这是我在数天前工作项目中需要解决的问题。

作为一名Web开发者,我当然知道 HTTPS 协议是保障用户敏感数据的好办法,但并不知道这种协议的内在工作机制。

它怎么保护数据?有人监听线路的情况下,服务器与客户端之间如何建立安全的连接?安全证书又是什么,为什么还要花钱买呢?

一系列通道

在深入讲解原理细节之前,让我们首先简单了解下HTTPS所防范的的问题,以及安全连接为何如此重要吧。

在你访问自己喜欢的站点时,从你的电脑发送的请求会在各个不同的网络之间传递——这些网络很有可能是用来偷听,甚至篡改你的信息。

局域网中,信息从你的电脑传输到其他电脑,传输到接入点,到ISP的路由器、交换机,最后到达骨干网线路。这样的一个过程中,有许多不同的组织在传送着你的请求。这时,如果不怀好意的用户侵入这条线路之中的任何一个系统中时,他们将很有可能看到线路中传送的内容。

而一般情况下,Web请求和相应都经由普通的HTTP协议明文传送。HTTP协议默认不使用加密协议,都是由于这些原因:

  • 加密消耗了很多计算资源。
  • 加密占用了更多的传输带宽。
  • 加密后缓存机制会失效。

不过,Web开发者会时不时遇到在连接中传送密码、信用卡号等敏感信息的情况。所以,有必要为这些页面做好防嗅探的准备措施。

传输层安全协议(TLS)

虽然下面讲解的内容和密码学有关,但是这里只是一个简单的介绍,不熟悉相关知识也应该看得懂。在实践中,是密码学算法确保了通信过程的安全,同时也抵御了潜在的信息黑手——干扰通信和监听的人。

SSL协议的继任者——TLS协议,常被用来实现安全HTTP连接(HTTPS)协议。在OSI网络模型中,TLS协议比HTTP协议的工作更加底层。确切来说,就是TLS的那部分连接发生在HTTP的连接之前。

TLS是一种混合的加密机制。它具有多种范式,接下来所看到的是对于这两种范式(用于共享秘密信息和身份认证(确保声称的身份和实际身份一致)的公钥算法和用于加密请求与回应机密信息的对称式算法)的说明:

公钥加密机制

使用公钥加密机制,双方各自拥有一份公钥和一份私钥,公钥和私钥通过数学演算联系在一起。公钥用于将明文转化为密文(变成了一堆乱码),私钥用来解密这一堆乱码般的信息。

一旦信息被公钥加密,它将只能由相应的私钥解密。两者缺一不可,而且也不能反过来使用。公钥可以自由传播,无需担心系统安全性降低;但私钥应妥善保管,不可将其泄露给未经授权解密的信息的用户,这就是公钥和私钥这两个名称的由来。

公钥机制相当酷的地方在于,通信双方可以在最初不安全的通道上建立起安全可靠的通信连接。

客户端和服务器都可以使用各自的私钥,只要共享了一部分公开信息,也就是共用了同一个公钥的情况下,就可以建立起相应的会话。

这意味着即使有人坐在客户端或者服务器前查看连接过程,他们也不会知道客户端或者服务的的私钥,也不会知道会话所共享的密码。

这得靠什么实现?靠数学!

Diffie-Hellman

这种密钥交换最常使用是Diffie-Hellman的密钥交换法。这项过程允许服务器和客户端双方商定共同的保密信息,而不需要在传输过程中交换这个信息。这样一来,即使嗅探者查看每个数据包,也不能确定连接上传输的共享密码是什么。

在最初的DH式密钥交换发生之后所生成的共享信息,可以在会话接下来的通信中使用更简洁的对称式加密法,我们之后就会看到对称式加密法的说明。

一点点数学

公钥算法的特点就是很容易由算子计算出结果,而基本上不可能作逆向运算。这也就是使用了两个质数的所要达到的目的。

现在假设Alice和Bob分别是参与DH式密钥交换过程的两方,他们一开始会商议确定一个小质数(一般是2,3,5这样的小数字)和一个大质数(有300位以上)作为加密的原始信息。小质数和大质数都可以直接传输,不必担心交换过程中的不安全。

需要明白的是,Alice和Bob各自都持有着自己的私钥(100多位的数),而且也永远不应该共享自己的私钥。不光是两人之间,也包括其他不相关的人都不应该拥有这两组私钥。网络中传输的是他们的私钥、小质数和大质数混合运算得到的结果。更确切来说,就是:

  • Alice的结果 = (小质数Alice的密码)% 大质数
  • Bob的结果 = (小质数Bob的密码)% 大质数
  • (“%” 符号表示取模运算,即取得除法运算的余数)

所以Alice使用大质数和小质数加上自己的私钥运算,就会得出结果,而Bob做同样的计算,也能得到相同的结果。当他们接收到对方的运算结果时,他们可以由数学计算导出会话中所要传输的信息,也就是说:

Alice计算的是

  • (Bob的结果Alice的密码)% 大质数

而Bob则计算

  • (Alice的结果Bob的密码)% 大质数

Alice和Bob得出来的数字相同,这个数字也就是会话中所要共享的密码。请注意,双方都没有向对方传输各自的私钥,而连接过程中也没有明文传递保密信息。这一点真是太棒了!

对数学不好的人而言,维基百科上有一张混合颜料的图可以用来说明情况:

请注意图中一开始的颜色(黄色)最后都会有Alice和Bob的颜色参与计算。这就是双方会得到同样结果的原因。对于观看中间过程的人来说,唯一在连接中发送的半合成信息是毫无意义的。

对称式加密机制

每次会话中只需要产生一次公钥交换的过程。在接受了同一个共享保密信息以后,服务器和客户端之间会使用更为高效的对称式加密机制进行通信,省去了来回交换的额外花销。

在接受了之前的共享保密信息之后,还会使用一套密码机制(一般是一组加密算法),使用共享的密码安全地通信,加密解密各自的信息。而窃听者只会看到一堆乱码在传来传去。

身份认证

DH式密钥交换允许双方创建私有的,共有的密码,但通信双方怎么确保是真正想要对话的人呢?这里就涉及到了身份认证的问题。

假设我拿起电话,跟我的朋友进行DH式密钥交换,在电话已经被干扰的情况,实际上是在跟其他人交换信息。在使用共享密码了以后,我仍然可以安全地与“朋友”交换信息,没有人可以解密我们的通信信息,但是“朋友”并不真的是我的朋友,这可是十分不安全的!

要解决身份认证问题,需要有配套的公钥基本设施,来核实用户的真实身份。这些设施用来创建,管理,发布,收回数字证书。而数字证书正是你需要为站点使用HTTPS协议付费的恼人事项。

但是,什么是数字证书,数字证书又是如何保证信息更加安全的呢?

证书

从更高的层次来讲,数字证书是将机器上的公钥和身份信息绑在一起的数字签名。数字签名担保某份公钥属于某个特定的组织和机构。

证书将域名(身份信息)和特定公钥关联起来。这就避免了窃听者将自己的服务器伪装成用户将要连接的服务器,并进行攻击的行为。

在上面打电话的例子中,攻击者可以尝试展示自己的公钥,装作是你的“朋友”,但是证书上面的签名信息便显示出:这份证书不是来自我信任的人的。

要受到一般浏览器的信任,证书本身还应当受到CA的信任。CA公司对认证会进行人工核查,确定申请主体满足以下两个条件:

  1. 在公共记录中存在着这个人/这家公司。
  2. 需要签名的证书上标明的域名的确由申请主体实际控制。

当CA查证,得出申请人属实,并且的确拥有这个域名的结果,CA便会为证书颁发签证,盖上“已核准”的戳记,表明网站的公钥属于这个网站,而且可以信任。

你的浏览器中会内置一系列受信任的CA列表。如果服务器返回的是未经过受信任CA签证的证书,浏览器会弹出大大的警告,这就在系统中多了一层安全措施,如果不然,任何人都可以四处签售伪造的证书。

这样一来,即使攻击者将自己的公钥拿出来,生成这份密钥,声称自己的伪造服务器就是“facebook.com”,浏览器也会因为检查到“未经受信任CA签名的证书”而弹出提示。

一些关于证书的其他事项

增强式认证

在常规的X.509 证书之外,增强式认证证书提供了更强力的认证。

要授予增强式认证证书,CA会对域名持有着做更加深入的查验(通常需要提供护照和水电费账单等信息)。

这种类型的证书,浏览器中大锁图标的显示位置背景也会变成绿色。

在同一台服务器上运行的多个网站

在HTTP协议连接开始之前进行的TLS协议握手流程,很有可能存在着多个网站存放在同一个服务器,使用相同IP地址的情况。

虚拟主机的Web路由是由Web服务器分发,但是TCP握手的过程,却是发生在连接之前。整个系统的单张证书会被发送到服务器的所有请求之中,这种流程会在共享主机的环境中发生问题。

如果你正在使用Web主机上提供的服务,他们会在你使用HTTPS协议之前要求使用独立的IP地址。不然主体提供商就需要每次在服务器上有新站点的时候,取得新证书(并且向CA重新申请认证)。 3 votes, average: 5.00 out of 53 votes, average: 5.00 out of 53 votes, average: 5.00 out of 53 votes, average: 5.00 out of 53 votes, average: 5.00 out of 5 (*3 个评分,平均: 5.00*)

Loading ... Loading ... 原文链接: hartley brody 翻译: 伯乐在线 - 伯乐在线读者 译文链接: http://blog.jobbole.com/45530/ [ 转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]

TCP IP传输层,你懂多少?

Posted on

TCP IP传输层,你懂多少? - - ITeye技术网站

首页 新闻 论坛 问答 博客 招聘 更多 ▼

专栏 群组 搜索

您还未登录 ! 我的应用 登录 注册

java-mzd

永久域名 http://java-mzd.iteye.com

141顶 25踩

TCP/IP网络层谜云 | Jpcap简易教程

2011-04-19

TCP/IP传输层,你懂多少?

文章分类:综合技术

你所不知道的传输层 题记:23页的文档上,满满当当的写满了笔记,纸质的东西,始终害怕丢失,还是选择把它总结到博客上来。

PS.老规矩,列出可能遇到的20个问题,如果您是都能回答的高手,请您绕道,我是小菜,只做自己的学习笔记。

  1. 传输层的主要功能是什么?
  2. 传输层如何区分不同应用程序的数据流?
  3. 传输层有哪些协议?
  4. 什么是UDP协议?
  5. 为什么有了UDP,还需要TCP?
  6. 什么是TCP协议?
  7. 怎么理解协议和程序?
  8. TCP是否真的有链接?
  9. 链接是如何建立的(逻辑上)?
  10. 所谓的建立TCP链接开销很大,具体是指什么?
  11. 三次握手的目的是什么?
  12. TCP如何提供可靠性?
  13. 什么是预期确认?什么是肯定确认与重新传输?哪些情况会重传?
  14. TCP中,序列号和应答号有哪些作用?
  15. TCP链接中,网络失败,是怎么判断的?
  16. 为什么需要窗口技术?
  17. 如何实现流量控制?
  18. UDP的开销很小,具体是指什么?
  19. UDP数据包、TCP数据包大小如何确认?
  20. UDP适合哪些环境?TCP适合哪些环境?

一。传输层的主要功能是什么?

分割并重新组装上层提供的数据流,为数据流提供端到端的传输服务。

二。传输层如何区分不同应用程序的数据流?


因为,对应传输层而言,它只需要知道目标主机上的哪个服务程序来响应这个程序,而不需要知道这个服务程序是干什么的。因此,我们只需要能够抽象的表示出来这些应用程序和服务程序即可。我们使用端口号来抽象标识每个网络程序。

传输层的TCP和UDP可以接收来自多个应用程序的数据流,用端口号标识他们,然后把他们送给Internet层处理;

同时TCP和UDP接收来自Internet层的数据包,用端口号区分他们,然后交给不同的应用程序。

因此:在同一IP地址(同一个目标主机)上不同的端口号是两个不同的链接。IP地址和端口号用来唯一的确定网络上数据的目的地。

三。传输层有哪些协议?

**传输层的两大协议:TCP(传输控制协议)UDP(用户数据包协议) TCP是一个可靠的面向链接的协议,UDP是不可靠的或者说无连接的协议。** 可以用打电话和发短信来说明这种关系:

UDP就好似发短信,只管发出去,至于对方是不是空号(网络不可到达)能不能收到(丢包)等并不关心。

TCP好像打电话,双方要通话,首先,要确定对方不是开机(网络可以到达),然后要确定是不是没有信号(),然后还需要对方接听(通信链接)。


四。什么是UDP协议?


UDP数据包结构如下图所示

源端口(16)

目标端口(16) 报文长度(16)

校验和(16) 数据(可变)

UDP为应用程序提供的是一种不可靠的、无连接的分组交付,因此,UDP报文可能会出现丢失、乱序、重复、延时等问题。 因为它不提供可靠性,它的开销很小。(开销很小具体指什么?下文揭秘)


五。为什么有了UDP,还需要TCP?

问题4中已经说到,UDP为应用程序提供的是一种无连接、不可靠的分组交付。当网络硬件失效或者负担太重时,数据包可能就会产生丢失、重复、延时、乱序的现象。这些都会导致我们的通信不正常。如果让应用程序来担负差错控制的工作,无疑将给程序员带来许多复杂的工作,于是,我们使用独立的通信协议来保证通信的可靠性是非常必要的。


六。什么是TCP协议?

传输控制协议TCP是一个面向链接的、可靠的通信协议。

  1. 在开始传输前,需要进行三次握手建立链接
  2. 可靠性:在传输过程中,通信双方的协议模块继续进行通信
  3. 通信结束后,通信双方都会使用改进的三次握手来关闭链接

TCP数据包结构如下图

源端口(16)

目标端口(16) 序号(32) 应答号(32) 头长度(4)

保留(6)

编码位(6)

窗口(16) 校验和(16)

紧急(16) 可选项(如果有,0或32) 数据(可变)

//七。怎么理解协议和程序?//

如同我们自定义的应用层协议一样:协议只是给出了一组规范,规定我们应该怎么样(按什么规则)保存数据。

在计算机间传输的永远都是二进制字节码(对于传输层,可以理解为传输的始终是下层的IP数据包),是**计算机中的程序通过对这些字节码进行逻辑分析、判断,来控制程序完成差错控制等功能。** 至于解析这些字节码的程序,则可以有不同的实现,只要我们按照规则来解析,并作出相应的控制,我们大可以自己写个程序是实现相应功能。

知道了这些后,显然,我们也可以使用前面说的Jpcap,来自己实现一个基于Java的TCP或者UDP协议。可以参考Linux下的Tcp源码。

/net/ipv4/udp.c /net/ipv4/datagram.c /net/ipv4/tcp_input.c /net/ipv4//tcp_output.c /net/ipv4/tcp.c

八。TCP是否真的有链接?

我们都知道,TCP通过完成三次握手来建立链接的,但是这种连接是面向虚电路的,是物理上不存在的,只是双方的TCP程序,逻辑上的认为建立了这样的链接**。 **


九。链接是如何建立的(逻辑上)?

假设:当我们在主机A上启动一个程序,通过TCP去链接主机B上的9091端口。

整个过程是怎么样的呢?逻辑上我们可以这么理解建立链接的过程:

1.SYN:seq=X;

1.1 A的TCP程序,为这个链接分配一个端口(设为9090)。 1.2 同时逻辑上的将TCP连接的状态设置为:正在连接。(通过在链接状态表中添加一条记录,记录中状态为:正在连接)

猜想:

TCP程序中, 应该有张表来保持链接的状态,其中每个状态应该有:

本机地址(IP加port)、对方地址、链接状态

1.3 同时,随机生成一个初始序列号X,生成一个TCP包,将初始化序列号X设置为TCP中的序列号,发送给主机B。

2.SYN:seq=Y ACK:ack=X+1;

2.1 B上TCP程序收到该数据包,查询9091端口状态,如果可以链接。 2.2 同样的,在逻辑上的将TCP连接的状态设置为:正在连接 2.3 同时,随机生成一个初始化序列号Y,根据接收的序列号X,生成应答号X+1,生成一个TCP包,将序列号和应答号分别设置到TCP包头中,将TCP数据包发给主机A。

3.SYN:seq=X+1 ACK:ack=Y+1.

3.1 A上的TCP程序接收到数据包,查询9090端口状态。 3.2 根据收到的SYN:seq=Y;ACK:ack=X+1; 封装一个TCP包 SYN:seq=x+1;ACK:ack=Y+1;发送给主机B。同时,TCP程序将链接状态表中该条记录状态设置为已连接。 3.3 主机B收到数据包,TCP程序将链接状态表中该条记录状态设置为已连接。

至此,一个TCP链接建立(三次握手)完成。 我们可以看到: 第一:传送的都是IP数据包,其实只是将收到的数据包交给TCP程序处理。 第二:链接状态,只是TCP程序中的一个逻辑状态。

十:所谓的建立TCP链接开销很大,具体是指什么?

从九中,很容易看出。要简历TCP链接,必须进行三次IP数据包的成功传输。

十一:三次握手的目的是什么?

TCP是面向链接的,在面向链接的环境中,开始传输数据之前,在两个中端之间必须先建立一个链接。建立链接的过程可以确保通信双方在发送应用程序数据包之前,都已经准备好了传送和接收数据。并且使通信双方统一了初始化序列号。

十二:TCP如何提供可靠性?

在传输过程中,通信双方的协议模块继续进行通信,从而确保了传输的可靠性。 针对乱序:在通过三次握手进行链接时,序列号被初始化。在传输过程中,TCP继续使用这个序列号来标记发送的每一个数据段,没传送一个数据段,序列号加一。接收方依据序列号重装收到的数据段。 针对丢包:在传输过程中,接收方收到一个数据段后,会用ACK应答码向发送端回复一个IP包进行应答,确认号ACK用来告诉发送端哪些数据包已经成功接收,发送方对未被应答的报文段提供重传。 针对重复:接收端收到数据段后,查看序列号,如果已经成功接收改数据包,则丢弃后面这个数据段。 针对延时:延时造成的第一个问题,就是数据包达到接收端时乱序。 当延时严重时,接收端一直未收到数据段,则不会回复ACK,发送端认为丢包,重发。

十三:什么是预期确认?什么是肯定确认与重新传输?哪些情况会重传?

1.确认号ACK会告诉发送端哪些数据段已经成功接收,并且确认号会向发送端指出接收端希望收到的下一个序列号。即,确实号ACK为上个数据序列号+1,这种机制称为预期确认

2.为了提高效率,我们在发送端,将数据段保存在缓冲区中,直道发送端收到来自接收端的确认号。这种机制被称为“肯定确认与重新传输”。

3.当发送端在给定时间间隔内收不到那个数据段的应答时,发送端就会重传那个数据段**。** 情况1:网络延时/环路,数据段丢失 情况2:网络延时,数据段推迟到达 情况3:数据段成功到达,应答因为1.2不能达到。

十四: TCP中,序列号和应答号有哪些作用?

从以上10,11,12中,很明显的可以看到

  1. 依靠序列号重组数据段

  2. 依靠数据包消除网络中的重复包
  3. 依靠序列号和应答号进行差错重传,提高了TCP的可靠性

十六:为什么需要窗口技术?

前面我们已经说了,TCP的可靠性,是通过预期确认来实现的。即发送方发送一个数据段后,需要得到对方的确认后,才会发送下一个数据段。 因此,假设一个数据段大小为64KB(IP包最大值),一次发送和确认需要的时间为500MS,则,1S内,只能传送128KB的数据,如果带宽为1M,显然很浪费带宽。为了充分利用带宽,我们使用窗口技术。滑动窗口允许发送方在收到接收方的确认之前发送多个数据段。(窗口大小决定了在收到确认前可以发送的数据段数量)

十七:如何实现流量控制?

窗口数决定了当前传输的最大流量。当我们在传输过程中,通信双方可以根据网络条件动态协商窗口大小,调整窗口大小时,即可实现流量控制。(在TCP的每个确认中,除了ACK外,还包括一个窗口通知)

十八:UDP的开销很小,具体是指什么?

1.因为UDP是无连接的。在传输数据之前,不需要进行复杂的三次握手来建立连接。 2.在传输数据时,没有协议间通信流量(确认信号),也不需要浪费不必要的处理时间(接收确认信号再发一下)。 3;传输结束后,也不用再用改进的三次握手来端口连接。

十九:UDP数据包、TCP数据包大小如何确认?

  1. 无论TCP还是UDP数据包,都需要交给Internet层封装为IP包,而一个IP包,包头中的长度位为16位,所以IP包最大为2的16方,即65535(64KB还需要减去各种包头长度)。

  2. TCP因为面向流,且可以凭借序列号对大文件进行分段和重组,因此,TCP可以用来传输较大的文件。而UDP,如果要传输大于64KB的数据,则需要自己在应用层进行差错控制。
  3. 为了提高传输效率和减少网络通信量(协议间的通信),TCP也会一次传输足够多的数据。
  4. 因为MTU的存在,TCP包和UDP包不是越大越好。(在路由中分包,在接收端重组,加大路由与接收端负担,增大丢包概率。分组丢失,整个数据包重传。)

二十:UDP适用哪些环境?TCP适用哪些环境?

适合UDP的环境: 1.在高效可靠的网络环境中(不需要考虑网络不好导致的丢包、乱序、延时、重复等问题),因为UDP是无连接的服务,不用消耗不必要的网络资源(TCP中的协议间通信)和处理时间(预期确认需要的时间),从而效率要高的多。 2.在轻权通信中,当需要传输的数据量很小(可以装在一个IP数据包内)时。如果我们使用TCP协议,那么,先建立连接,一共需要发送3个IP数据包,然后数据传输,1个IP数据包,产生一个确认信号的IP包,然后关闭连接,需要传输5个IP数据包。使用TCP协议IP包的利用率为1/10。而使用UDP,只需要发送一个IP数据包。哪怕丢包(服务不成功),也可重新申请服务(重传)。 注:而且无论UDP还是TCP,传输的都是IP数据包。当网络环境不好导致丢包时,无论TCP还是UDP都会丢包,这是没有区别的。(如果考虑发送丢包,那么TCP效率更低),只是使用TCP,当连接建立成功后,TCP程序会进行可靠性控制。 UDP很适合这种客户机向服务器传送简单服务请求的环境。此类应用层协议包括TFTP , SNMP , DNS ,DHCP等。 3.在对实时性要求很强的通信中:在诸如实时视频直播等对实时性要求很高的环境中,从而允许一定量的丢包的情况下(直播比赛,前面丢失的包,重传出来已经意义不大了),UDP更适合。(可以根据具体需要通过应用层协议提供可靠性,不用像TCP那么严格。)

适合TCP协议的环境:

当网络硬件失效或者负担太重时,数据包可能就会产生丢失、重复、延时、乱序的现象。这些都会导致我们的通信不正常的时候。如果让应用程序来担负差错控制的工作,无疑将给程序员带来许多复杂的工作,于是,我们使用独立的通信协议来保证通信的可靠性是非常必要的。

PS.写博客真累。。

特别是根据一堆读书笔记写博客。。

特别是写4-5000字的总结博客。。

累的感觉瘫痪了两次。。唉。。伤不起啊。。

TCP/IP网络层谜云 | Jpcap简易教程

评论

53 楼 lucane 2011-05-14 引用

十:所谓的建立TCP链接开销很大,具体是指什么? 从九中,很容易看出。要"简历"TCP链接,必须进行三次IP数据包的成功传输。 52 楼 xsz 2011-05-14 引用

顶顶楼主。

51 楼 wgh596 2011-05-14 引用

辛苦,辛苦。 50 楼 hbice 2011-05-14 引用

获益匪浅,lz辛苦,我也辛苦了,顶着牙疼看帖子。。。赶紧去看牙医了。。。

49 楼 Lagunarock 2011-05-14 引用

非常不错 涵盖面广,排版又好 值得学习 48 楼 adeline.pan 2011-05-13 引用

写得很好,上学时候学的差不多都忘了,看了又都想起来了,收藏

47 楼 qq376727939 2011-05-12 引用

好贴啊,dou zhu 46 楼 gubingo 2011-05-12 引用

议和程序?//

greatwqs 写道 总结的很好!

45 楼 greatwqs 2011-05-10 引用

总结的很好! 44 楼 嘻哈方式 2011-05-10 引用

这方面一直都是我的薄弱项 看后我的功力又增强啦! 多谢楼主!!!

43 楼 steafler 2011-05-08 引用

IP是网络层协议 42 楼 wuhuajun 2011-05-07 引用

41 楼 pan_java 2011-05-05 引用

顶楼主,不错! 40 楼 liuyes 2011-05-05 引用

写得很好哈

39 楼 beiyangshuishi 2011-05-03 引用

写的不错,增长知识了。以前我对网络知识一窍不通。 38 楼 gogopengyou 2011-05-03 引用

基本都看明白了,总结的挺好的

37 楼 小猪笨笨 2011-04-28 引用

哈哈,谢谢了,其实我对这些东西一直一知半解,看了你这篇文有很大收获 36 楼 torry_1979 2011-04-27 引用

作为一个普及性的资料还不错

35 楼 sswh 2011-04-27 引用

mark 谢谢分享。 34 楼 20444465 2011-04-26 引用

做it的一家要懂

« 上一页 1 2 3 下一页 »

发表评论

表情图标

字体颜色: 标准深红红色橙色棕色黄色绿色橄榄青色蓝色深蓝靛蓝紫色灰色白色黑色 字体大小: 标准1 (xx-small)2 (x-small)3 (small)4 (medium)5 (large)6 (x-large)7 (xx-large) 对齐: 标准居左居中居右

提示:选择您需要装饰的文字, 按上列按钮即可添加上相应的标签

您还没有登录,请登录后发表评论(快捷键 Alt+S / Ctrl+Enter)

java_mzd的博客

java_mzd

搜索本博客

最近访客 >>更多访客

zhxing的博客

zhxing

java_suddy的博客

java_suddy liuxinglanyue的博客

liuxinglanyue

libo_591的博客

libo_591

博客分类

其他分类

存档