Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1888025
  • 博文数量: 606
  • 博客积分: 9991
  • 博客等级: 中将
  • 技术积分: 5725
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-17 19:07
文章分类

全部博文(606)

文章存档

2011年(10)

2010年(67)

2009年(155)

2008年(386)

分类: Java

2009-11-09 15:43:17

一  总述

Stream Socket与Datagram Socket不同,它有一个永久性的链接,可以确保数据可靠的发送到对方。但是Stream Socket所占的资源更多。

Java主要通过Socket及ServerSocket来实现Stream Socket,一个用于客户端,另一个用户服务器端。

二  Socket类

Socket类的几个重要方法如下:

(1) Socket(String host, int port)

(2) Socket(InetAddress address, int port)

(3) synchronized void close()

(4) InputStream getInputStream()

(5) OutputStream getOutputStream()

可以看出Stream Socket中的Socket与Datagram Socket中的Socket的区别:

(1) 在构造函数中,这里需要指定服务器的地址与端口,用于它发送数据之前建立链接,而Datagram的Socket不需要建立链接,它仅需要在要发送的数据包中包含地址信息即可;

(2) Stream Socket的两个重要方法和其它流的方法类似,而且在操作上也相似,其实在操作Stream Socket时,只要建立了链接,那么以后的操作和流的操作一样了,你可以忘了你在操作Socket,只当做自己在操作普通的流。

三  ServerSocket类

ServerSocket有以下几个重要的方法:

(1) ServerSocket(int port):监听指定端口,监听时间为默认值50;

(2) ServerSocket(int port, int count):监听指定端口,并指定监听时间;

(3) ServerSocket(int port, int backlog, InetAddress bindAddr):只接收指定接口发过来的数据;

(4) Socket accept():accpet方法会返回一个Socket对象,使用该Socket对象的getInputStream及getOutputStream方法获取输入及输出流。由此可见无论是服务器端还是客户端,它们都是通过Socket对象来收发数据的。程序执行到accpet时会进入监听状态,不再向下执行一直到接收到数据。

(5) void close()

四 相关描述

/套接字

   提供了在主机之间传递原始字节流的功能,以比较底层的方式访问tcp/ip协议层.可以类似访问文件i/o的方式实现这一功能(在unix 中,系统资源是以文件的方式进行访问的,其中也包括网络资源).tcp/ip协议规定,每台主机都有一个名称,例如 roedy.mindprod.com.然而,同一台主机上有可能同时运行很多tcp/ip程序.每个socket被指派了一个叫做端口的数字以加以区分 不同的应用或者连接.http应用服务器的端口一般被指定为80,DbAnywhere通常指定为8889.我们通过这种方式区分你向远程或者本地主机请 求连接的服务.一个socket被定义为 地址:端口, 例如 roedy.mindprod.com:8889


Flush / 刷新

   如果向一个写入数据,通常需要调用flush方法去把数据发送到网络.如果操作失败,可能由于完整的请求信息未曾发送成功而导致持续等待响应.如果使用稳定的数据流的方式,不需要调用flush方法,因为数据流会自动把先前的数据发送到网络.

Blocking Read / 读堵塞

   由读取数据时,如果使用堵塞的读操作,可能会导致永久地等待.Socket的setSoTimeout方法 控制了超时的期限.在socket连接失败的情况下,读取数据的操作最终会被停止.

这种情况通常发生在以下几种情况:

   1.本地关闭了,

   2.远程主机/终端发送了断开连接的信号,

   3.tcp协议实现在尝试多次重发数据仍无法获得对方针对已发送数据包的确认信息,或者无法获得keep-alive的信息(如果tcp协议的keep- alive选项已经被启用).另外不要和http协议的keep-alive参数相混淆.(http的keep-alive选项是指客户端与服务器之间建 立有效的长连接,避免了重复建立连接的消耗,尤其对提供静态资源访问的网站能够很大的提高访问效率)

Timeouts /超时

 

   的java实现接口提供了setSoTimeout方法设置希望等待完成读取操作的时间期限,提供setSoLinger方法控制关闭等待期 限(等待尚未发送的数据,然后关闭连接). 当一方关闭连接时,另一方仍会在读取到缓冲区中的通知关闭连接的数据以后关闭连接(这句话不知道这样翻译是否准确,不过实际操作应该是这样的,可以这样理 解,当一端单方面关闭连接的时候,应该通知另一方你已经关闭连接,以便对方获悉并且关闭连接).setSoTimeout选项对等待完成写操作的期限没有 影响(等待对方对方接收数据的期限),只和等待对方产生数据的期限有关.(setSoTimeout和对方发送响应数据是否超时有关和对方何时接收数据没 有关系).

   比较令人苦闷的是,windows系统不负责任地忽略超时.对于一个连接.java虚拟机努力将域名解析为ip地址和端口号.而windows使用udp 的137端口向域名解析服务器发送域名解析查询,超时设为1.5秒.忽略了任何的icmp端口不可访问的数据包并且连续再重复发送两次相同的请求(一共是 三次).总计需要等待4.5秒.因此强烈建议把常用的域名地址和对应的ip地址和端口写在hosts文件中以确保可以迅速解析.另外就是在局域网完全中关 闭windows的NETBIOS服务,完全使用tcp/ip访问资源.

Disconnet Detection / 探测连接关闭

   当网络繁忙的时候,tcp/ip无法发送数据包.如果没有设定的setKeepAlive为true,我们无法获悉一个连接已经关闭除非试图 再次进行发送操作(或者进行某些接收操作).java通过设定socket的setKeepAlive为true的方式要求tcp/ip协议进行心跳检 测,不需要发送任何数据包或者应用级别的.然而不幸地是你无法肯定tcp/ip以怎样的频率发送心跳探测信号.如果另一方无法及时响应,当你试图进行 读取操作的时候就会产生socket的异常.心跳包使双方都能获知对方是否保持连接.心跳包只是一个普通的tcp/ip的ack报文不需要搭载任何的其他 数据.

   当应用处于空闲状态的时候,你的应用可以间断地向彼此发送小的心跳信息.接收者可以完全忽视它们,但是它们强制tcp/ip协议去核实另一方是否存活.这 不是tcp/ip协议通信规范的一部分,你需要建立自己的心跳协议,例如 发送内容为' are-you-still-alive? '的信息,原作者通过测试发现java的连接持续性并非100%的可靠.他的银弹技术是通过服务端每隔30秒发送一个应用级别的心跳包,如果最近30秒内 没有接收到任何数据包.服务器必须每隔30秒发送一个数据包,不一定必须是傀儡的心跳数据包.心跳数据包只当服务器空闲的时候才会产生.否则的话,普通的 网络通信就可以替代心跳数据包的功能.applet探测发现由于断开连接导致的通信中断后就会重新建立连接.负面影响是你的应用必须时时关注这些心跳状 态,并且如果你使用其它网络协议你也要实现相应的心跳协议,不同余依赖于tcp/ip层的心跳.

   然而,使用内置的setKeepAlive(true)方法去要求tcp/ip进行心跳探测不使用任何数据包或者应用级别地实现看起来更加容易一些.每个终端只需间歇地发送一个包含当前序列的空的数据包,确认信息和滑动窗口号就可以了.

    应用级别的心跳优点在于它们能够使你了解两端的应用都是否存活,而不在于只是通信软件.

Server Side Socketing /服务器端套接字

   对于一个接收外部连接的服务器,首先在某个没有连接任何客户端的端口上开启一个server,代码如下


ServerSocket serverSocket = new ServerSocket( port );

Then it calls accept, which blocks until a call comes in.

Socket clientSocket = serverSocket.accept();

   当接收到一个请求时会新建一个的普通的,通常服务器会启动一个线程或者由线程池中取出一个线程处理新产生的socket,然后循环处理下一个请求.

   即使你并不拥有一个域名,你也可以建立自己的服务器.他人可以通过ip地址和端口的方式( e.g. 65.110.21.43:2222)访问你的服务器(如果在广域网上这要求你拥有自己的固定ip,这一般比拥有域名的成本还要高,不过在局域网内你可以 尝试局域网地址),如果你在处于防火墙保护的局域网内,你可以使用防火墙的对外ip.你必须配置你的防火墙以便请求数据包可以通过并且访问局域网内正确的 服务器.

Flow Control / 流控制

   使用的setReceiveBufferSize()方法你可以告诉底层的操作系统缓存多大的接收数据.但是这并非完全由你决定.不要将 socket的缓冲区和BufferedInputStream的缓冲区混淆.这是原始socket的底层的缓冲区.过大的缓冲区并不总能很好地满足需 要.使用小的缓冲区能够通知另一端你的处理速度已经落后了,因此对方不会继续马上发送数据过来(大的缓冲区,对方发送过来的数据有可能还没有读取并被处 理,但还留有很大的空间,因此对方会继续发送数据填满余下的空间,但是有可能导致大量的数据堆积在缓冲区中无法处理,理想状态是使用小的缓存区,处理完当 前数据后在接收,处理下一个数据).如果数据不是实时的,发送过来的数据量动态地依赖于处理数据的速度,过大的缓冲区会导致你处理的数据量一直落后于接收 的数据量,并且永远无法赶上.

     的setTcpNoDelay( true )很神秘地用来关闭Nagle算法.正如 这里不解释Nagle算法一样,这里也不讨论这个setTcpNoDelay方法. 如果你处理近乎实时的数据,你可能会研究如何关闭Nagle算法.Nagle算法通过暂存已经提交发送的数据包许多毫秒的时间以便判断是否还需要向这个数 据包写入更多数据,确保tcp不发送大量的长度过小的数据包.我不确定是否flush方法能够充分地立即发送一个数据包.

Graceful Shutdown / 优雅地关闭

   如果你简单地关闭一个连接,你可能会丢失先前发送但并未抵达(交付)的数据.这可能会导致数据不完整.所以,如果优雅地关闭连接呢?作者的理 论是:当客户端试图关闭连接时,它首先要发送一条关闭信息.服务器原样返回关闭信息内容和确认关闭信息(增加确认关闭信息的做法可能是为了避免发送超时的 数据包返回给发送者,两者内容可能是相同的),客户端收到确认信息后关闭连接.这时客户端要确保解除等待读取操作的状态,并且你要确保客户端和服务器在关 闭前都收到了最后的信息.

五  实例



六 相关资料

阅读(1065) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~