日期:2011-03-22 16:16:00 来源:本站整理
<b>java数据报编程</b>[Java编程]
本文“<b>java数据报编程</b>[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
大家迄今看到的例子利用的都是“传输掌握协议”(TCP),亦称作“基于数据流的套接字”.按照该协议的计划目标,它具有高度的坚固性,并且能保证数据顺利到达目的地.换言之,它答应重传那些由于各种缘由半路“走失”的数据.并且收到字节的次序与它们发出来时是一样的.当然,这种掌握与坚固性需求我们付出一些代价:TCP具有非常高的开销.
还有另一种协议,名为“用户数据报协议”(UDP),它并不决心追求数据包会完好发送出去,也不能担保它们到达的次序与它们发出时一样.我们认为这是一种“不坚固协议”(TCP当然是“坚固协议”).听起来仿佛很糟,但由于它的速度快得多,所以常常还是有效武之地的.对某些利用来说,比方声音信号的传输,假如少量数据包在半路上丧失了,那么用不着太在乎,因为传输的速度显得更重要一些.大大都互联网游戏,如Diablo,采取的也是UDP协议通信,因为网络通信的快慢是游戏能否流利的决意性因素.也可以想想一台报时服务器,假如某条消息丧失了,那么也真的没必要过份慌张.别的,有些利用大概能向服务器传回一条UDP消息,以便今后可以恢复.假如在得当的时间里没有呼应,消息就会丧失.
Java对数据报的支持与它对TCP套接字的支持大致相同,但也存在一个明显的辨别.对数据报来说,我们在客户和服务器程序都可以安排一个DatagramSocket(数据报套接字),但与ServerSocket差别,前者不会干巴巴地等候成立一个衔接的恳求.这是由于不再存在“衔接”,取而代之的是一个数据报摆设出来.另一项本质的辨别的是对TCP套接字来说,一旦我们建好了衔接,便不再需求关心谁向谁“说话”——只需通过会话流往复传送数据便可.但对数据报来说,它的数据包必须知道自己来自何处,以及打算去那边.这意味着我们必须知道每个数据报包的这些信息,不然信息就不能正常地传送.
DatagramSocket用于收发数据包,而DatagramPacket包含了具体的信息.预备接纳一个数据报时,只需供应一个缓冲区,以便安设接纳到的数据.数据包到达时,通过DatagramSocket,作为信息发源地的因特网地址以及端口编号会自动得到初化.所以一个用于接纳数据报的DatagramPacket构建器是:
DatagramPacket(buf, buf.length)
此中,buf是一个字节数组.既然buf是个数组,大家大概会奇特为什么构建器自己不能调查出数组的长度呢?实际上我也有同感,唯一能猜到的缘由就是C气势的编程使然,那边的数组不能自己奉告我们它有多大.
可以反复利用数据报的接纳代码,没必要每次都建一个新的.每次用它的时刻(再生),缓冲区内的数据城市被覆盖.
缓冲区的最大容量仅受限于答应的数据报包大小,这个限制位于比64KB稍小的地方.但在很多利用程序中,我们都甘愿它变得还要小一些,分外是在发送数据的时刻.具体挑选的数据包大小取决于利用程序的特定要求.
发出一个数据报时,DatagramPacket不但需求包含正式的数据,也要包含因特网地址以及端口号,以决意它的目的地.所以用于输出DatagramPacket的构建器是:
DatagramPacket(buf, length, inetAddress, port)
这一次,buf(一个字节数组)已经包含了我们想发出的数据.length可以是buf的长度,但也可以更短一些,意味着我们只想发出那么多的字节.另两个参数辨别代表数据包要到达的因特网地址以及目标机械的一个目标端口(注释②).
②:我们认为TCP和UDP端口是彼此独立的.也就是说,可以在端口8080同时运行一个TCP和UDP服务程序,二者之间不会产生冲突.
大家大概认为两个构建器成立了两个差别的对象:一个用于接纳数据报,另一个用于发送它们.假如是好的面向对象的计划筹划,会倡议把它们成立成两个差别的类,而不是具有差别的行为的一个类(具体行为取决于我们若何构建对象).这大概会成为一个严重的问题,但幸运的是,DatagramPacket的利用相当简单,我们不需求在这个问题上胶葛不清.这一点在下例里将有很明确的阐明.该例近似于前面针对TCP套接字的MultiJabberServer和MultiJabberClient例子.多个客户城市将数据报发给服务器,后者会将其反馈回最初发出消息的一样的客户.
为简化从一个String里成立DatagramPacket的工作(大概从DatagramPacket里成立String),这个例子首先用到了一个工具类,名为Dgram:
//: Dgram.java // A utility class to convert back and forth // Between Strings and DataGramPackets. import java.net.*; public class Dgram { public static DatagramPacket toDatagram( String s, InetAddress destIA, int destPort) { // Deprecated in Java 1.1, but it works: byte[] buf = new byte[s.length() + 1]; s.getBytes(0, s.length(), buf, 0); // The correct Java 1.1 approach, but it's // Broken (it truncates the String): // byte[] buf = s.getBytes(); return new DatagramPacket(buf, buf.length, destIA, destPort); } public static String toString(DatagramPacket p){ // The Java 1.0 approach: // return new String(p.getData(), // 0, 0, p.getLength()); // The Java 1.1 approach: return new String(p.getData(), 0, p.getLength()); } } ///:~
Dgram的第一个办法采取一个String、一个InetAddress以及一个端口号作为自己的参数,将String的内容复制到一个字节缓冲区,再将缓冲区传送进入DatagramPacket构建器,从而构建一个DatagramPacket.注意缓冲辨别配时的"+1"——这对避免截尾现象是非常重要的.String的getByte()办法属于一种特别操作,能将一个字串包含的char复制进入一个字节缓冲.该办法目前已被“反对”利用;Java 1.1有一个“更好”的办法来做这个工作,但在这里却被当作注释屏蔽掉了,因为它会截掉String的部份内容.所以固然我们在Java 1.1下编译该程序时会得到一条“反对”消息,但它的行为仍旧是精确无误的(这个错误应当在你读到这里的时刻改正了).
Dgram.toString()办法同时展示了Java 1.0的办法和Java 1.1的办法(二者是差别的,因为有一种新范例的String构建器).
下面是用于数据报演示的服务器代码:
//: ChatterServer.java // A server that echoes datagrams import java.net.*; import java.io.*; import java.util.*; public class ChatterServer { static final int INPORT = 1711; private byte[] buf = new byte[1000]; private DatagramPacket dp = new DatagramPacket(buf, buf.length); // Can listen & send on the same socket: private DatagramSocket socket; public ChatterServer() { try { socket = new DatagramSocket(INPORT); System.out.println("Server started"); while(true) { // Block until a datagram appears: socket.receive(dp); String rcvd = Dgram.toString(dp) + ", from address: " + dp.getAddress() + ", port: " + dp.getPort(); System.out.println(rcvd); String echoString = "Echoed: " + rcvd; // Extract the address and port from the // received datagram to find out where to // send it back: DatagramPacket echo = Dgram.toDatagram(echoString, dp.getAddress(), dp.getPort()); socket.send(echo); } } catch(SocketException e) { System.err.println("Can't open socket"); System.exit(1); } catch(IOException e) { System.err.println("Communication error"); e.printStackTrace(); } } public static void main(String[] args) { new ChatterServer(); } } ///:~
ChatterServer成立了一个用来接纳消息的DatagramSocket(数据报套接字),而不是在我们每次预备接纳一条新消息时都新建一个.这个单一的DatagramSocket可以反复利用.它有一个端口号,因为这属于服务器,客户必须切当知道自己把数据报发到哪个地址.固然有一个端口号,但没有为它分配因特网地址,因为它就驻留在“这”台机械内,所以知道自己的因特网地址是什么(目前是默许的localhost).在无限while循环中,套接字被奉告接纳数据(receive()).然后暂时挂起,直到一个数据报呈现,再把它反馈回我们但愿的接纳人——DatagramPacket dp——里面.数据包(Packet)会被转换成一个字串,同时插入的还有数据包的发源因特网地址及套接字.这些信息会显示出来,然后增添一个额外的字串,指出自己已从服务器反馈回来了.
大家大概会认为有点儿迷惑.正如大家会看到的那样,很多差别的因特网地址和端口号都大概是消息的发源地——换言之,客户程序大概驻留在任何一台机械里(就这一次演示来说,它们都驻留在localhost里,但每个客户利用的端口编号是差别的).为了将一条消息送回它真正的始发客户,需求知道那个客户的因特网地址以及端口号.幸运的是,全部这些资料均已非常周到地封装到发出消息的DatagramPacket内部,所以我们要做的全部事情就是用getAddress()和getPort()把它们取出来.操纵这些资料,可以构建DatagramPacket echo——它通过与接纳用的相同的套接字发送回来.除此以外,一旦套接字发出数据报,就会增添“这”台机械的因特网地址及端口信息,所以当客户接纳消息时,它可以操纵getAddress()和getPort()理解数据报来自何处.事实上,getAddress()和getPort()唯一不能奉告我们数据报来自何处的前提是:我们成立一个待发送的数据报,并在正式发出之前调用了getAddress()和getPort().到数据报正式发送的时刻,这台机械的地址以及端口才会写入数据报.所以我们得到了应用数据报时一项重要的原则:没必要跟踪一条消息的根源地!因为它必定保存在数据报里.事实上,对程序来说,最坚固的做法是我们不要试图跟踪,而是无论若何都从目标数据报里提取出地址以及端口信息(就象这里做的那样).
为测试服务器的运转能否正常,下面这程序将成立大量客户(线程),它们城市将数据报包发给服务器,并等候服务器把它们原样反馈回来.
//: ChatterServer.java // A server that echoes datagrams import java.net.*; import java.io.*; import java.util.*; public class ChatterServer { static final int INPORT = 1711; private byte[] buf = new byte[1000]; private DatagramPacket dp = new DatagramPacket(buf, buf.length); // Can listen & send on the same socket: private DatagramSocket socket; public ChatterServer() { try { socket = new DatagramSocket(INPORT); System.out.println("Server started"); while(true) { // Block until a datagram appears: socket.receive(dp); String rcvd = Dgram.toString(dp) + ", from address: " + dp.getAddress() + ", port: " + dp.getPort(); System.out.println(rcvd); String echoString = "Echoed: " + rcvd; // Extract the address and port from the // received datagram to find out where to // send it back: DatagramPacket echo = Dgram.toDatagram(echoString, dp.getAddress(), dp.getPort()); socket.send(echo); } } catch(SocketException e) { System.err.println("Can't open socket"); System.exit(1); } catch(IOException e) { System.err.println("Communication error"); e.printStackTrace(); } } public static void main(String[] args) { new ChatterServer(); } } ///:~
ChatterClient被成立成一个线程(Thread),所以可以用多个客户来“骚扰”服务器.从中可以看到,用于接纳的DatagramPacket和用于ChatterServer的那个是类似的.在构建器中,成立DatagramPacket时没有附带任何参数(自变量),因为它不需求明确指出自己位于哪个特定编号的端口里.用于这个套接字的因特网地址将成为“这台机械”(比方localhost),并且会自动分配端口编号,这从输出后果便可看出.同用于服务器的那个一样,这个DatagramPacket将同时用于发送和接纳.
hostAddress是我们想与之通信的那台机械的因特网地址.在程序中,假如需求成立一个预备传出去的DatagramPacket,那么必须知道一个精确的因特网地址和端口号.可以必定的是,主机必须位于一个已知的地址和端口号上,使客户能启动与主机的“会话”.
每个线程都有自己举世无双的标识号(固然自动分配给线程的端口号是也会供应一个唯一的标识符).在run()中,我们成立了一个String消息,此中包含了线程的标识编号以及该线程预备发送的消息编号.我们用这个字串成立一个数据报,发到主机上的指定地址;端口编号则直接从ChatterServer内的一个常数获得.一旦消息发出,receive()就会暂时被“堵塞”起来,直到服务器答复了这条消息.与消息附在一同的全部信息使我们知道回到这个特定线程的东西恰是从始发消息中送达出去的.在这个例子中,固然是一种“不坚固”协议,但仍旧可以查抄数据报能否到去过了它们该去的地方(这在localhost和LAN环境中是成立的,但在非本地衔接中却大概呈现一些错误).
运行该程序时,大家会发现每个线程城市完毕.这意味着发送到服务器的每个数据报包城市反转展转,并反馈回精确的接纳者.假如不是这样,一个或更多的线程就会挂起并进入“堵塞”状况,直到它们的输入被显暴露来.
大家大概认为将文件从一台机械传到另一台的唯一精确方法是通过TCP套接字,因为它们是“坚固”的.但是,由于数据报的速度非常快,所以它才是一种更好的挑选.我们只需将文件分割成多个数据报,并为每个包编号.接纳机械会获得这些数据包,并重新“组装”它们;一个“标题包”会奉告机械应当接纳多少个包,以及组装所需的另一些重要信息.假如一个包在半路“走丢”了,接纳机械会返回一个数据报,奉告发送者重传.
以上是“<b>java数据报编程</b>[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |
- ·上一篇文章:java的线程死锁
- ·下一篇文章:服务多个客户(java)
- ·中查找“<b>java数据报编程</b>”更多相关内容
- ·中查找“<b>java数据报编程</b>”更多相关内容
评论内容只代表网友观点,与本站立场无关!
评论摘要(共 0 条,得分 0 分,平均 0 分)
查看完整评论