think in java 读书笔记 3 —— 数据报
目录
概要
1. 数据报基本知识
之前套接字中例子使用的都是“传输控制协议”(TCP),亦称作“基于数据流的套接字”。根据该协议的设计宗旨,它具有高度的可靠性,而且能保证数据顺利抵达目的地。换言之,它允许重传那些由于各种原因半路“走失”的数据。而且收到字节的顺序与它们发出来时是一样的。当然,这种控制与可靠性需要我们付出一些代价:TCP 具有非常高的开销。
还有另一种协议,名为“用户数据报协议”(UDP),它并不刻意追求数据包会完全发送出去,也不能担保它们抵达的顺序与它们发出时一样。我们认为这是一种“不可靠协议”(TCP 当然是“可靠协议”)。
Java 对数据报的支持与它对TCP 套接字的支持大致相同,但也存在一个明显的区别。对数据报来说,我们在客户和服务器程序都可以放置一个DatagramSocket(数据报套接字),但与ServerSocket 不同,前者不会干巴巴地等待建立一个连接的请求。这是由于不再存在“连接”,取而代之的是一个数据报陈列出来。另一项本质的区别的是对TCP 套接字来说,一旦我们建好了连接,便不再需要关心谁向谁“说话”——只需通过会话流来回传送数据即可。但对数据报来说,它的数据包必须知道自己来自何处,以及打算去哪里。这意味着我们必须知道每个数据报包的这些信息,否则信息就不能正常地传递。DatagramSocket 用于收发数据包,而DatagramPacket 包含了具体的信息。准备接收一个数据报时,只需提供一个缓冲区,以便安置接收到的数据。数据包抵达时,通过DatagramSocket,作为信息起源地的因特网地址以及端口编号会自动得到初化。
所以一个用于接收数据报的DatagramPacket 构建器是:
DatagramPacket(buf, buf.length)
可以重复使用数据报的接收代码,不必每次都建一个新的。每次用它的时候(再生),缓冲区内的数据都会被覆盖。发出一个数据报时,DatagramPacket 不仅需要包含正式的数据,也要包含因特网地址以及端口号,以决定它的目的地。
所以用于输出DatagramPacket 的构建器是:
DatagramPacket(buf, length, inetAddress, port)
这一次,buf(一个字节数组)已经包含了我们想发出的数据。length 可以是buf 的长度,但也可以更短一些,意味着我们只想发出那么多的字节。我们认为TCP 和UDP 端口是相互独立的。也就是说,可以在端口8080 同时运行一个TCP 和UDP 服务程序,两者之间不会产生冲突。
该例类似于前面针对TCP 套接字的
MultiJabberServer 和MultiJabberClient 例子。多个客户都会将数据报发给服务器,后者会将其反馈回最
初发出消息的同样的客户。
2. 服务器端和客户端程序实例
该例类似于前面针对TCP 套接字的MultiJabberServer 和MultiJabberClient 例子。多个客户都会将数据报发给服务器,后者会将其反馈回最初发出消息的同样的客户。为简化从一个String 里创建DatagramPacket 的工作(或者从DatagramPacket 里创建String),这个例子首先用到了一个工具类,名为Dgram:
package com.xingle_test.datagram; import java.net.DatagramPacket;
import java.net.InetAddress; /**
* @ClassName: Dgram
* @Description: 从一个String 里创建DatagramPacket 的工作(或者从DatagramPacket 里创建String)
* @author xingle
* @date 2014年7月25日 下午11:05:03
*/
public class Dgram {
public static DatagramPacket toDatagram(String s, InetAddress destIA,
int destPort) { byte[] buf = s.getBytes();
return new DatagramPacket(buf, buf.length, destIA, destPort);
} public static String toString(DatagramPacket p) { return new String(p.getData(), 0, p.getLength());
}
}
数据报演示的服务器代码:
package com.xingle_test.datagram; import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.SocketException; /**
* 数据报 服务器端
*
* @ClassName: ChatterServer
* @author Xingle
* @date 2014-7-23 下午3:42:09
*/
public class ChatterServer { static final int INPORT = 8080;
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)会被转换成一个字串,同时插入的还有数据包的起源因特网地址及套接字。这些信息会显示出来,然后添加一个额外的字串,指出自己已从服务器反馈回来了。
为了将一条消息送回它真正的始发客户,需要知道那个客户的因特网地址以及端口号。幸运的是,所有这些资料均已非常周到地封装到发出消息的DatagramPacket 内部,所以我们要做的全部事情就是用getAddress()和getPort()把它们取出来。利用这些资料,可以构建DatagramPacket echo——它通过与接收用的相同的套接字发送回来。除此以外,一旦套接字发出数据报,就会添加“这”台机器的因特网地址及端口信息,所以当客户接收消息时,它可以利用getAddress()和getPort()了解数据报来自何处。事实上,getAddress()和getPort()唯一不能告诉我们数据报来自何处的前提是:我们创建一个待发送的数据报,并在正式发出之前调用了getAddress()和getPort()。到数据报正式发送的时候,这台机器的地址以及端口才会写入数据报。所以我们得到了运用数据报时一项重要的原则:不必跟踪一条消息的来源地!因为它肯定保存在数据报里。
为测试服务器的运转是否正常,下面这程序将创建大量客户(线程),它们都会将数据报包发给服务器,并等候服务器把它们原样反馈回来。
客户端:
package com.xingle_test.datagram; import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException; /**
* 客户端(线程)
* @ClassName: ChatterClient
* @author Xingle
* @date 2014-7-25 下午5:22:52
*/
public class ChatterClient extends Thread { private DatagramSocket s;
private InetAddress hostAddress;
private byte[] buf = new byte[1000];
private DatagramPacket dp = new DatagramPacket(buf, buf.length);
private int id; public ChatterClient(int identifier) {
id = identifier;
try {
// Auto-assign port number:
s = new DatagramSocket();
hostAddress = InetAddress.getByName("localhost");
} catch (UnknownHostException e) {
System.err.println("Cannot find host");
System.exit(1);
} catch (SocketException e) {
System.err.println("Can't open socket");
e.printStackTrace();
System.exit(1);
}
System.out.println("ChatterClient starting");
} public void run() {
try {
for (int i = 0; i < 5; i++) {
String outMessage = "---Client #" + id + ", message #" + i+"---";
// Make and send a datagram:
s.send(Dgram.toDatagram(outMessage, hostAddress,
ChatterServer.INPORT));
// Block until it echoes back:
s.receive(dp);
// Print out the echoed contents:
String rcvd = "Client #" + id + ", rcvd from "
+ dp.getAddress() + ", " + dp.getPort() + ": "
+ Dgram.toString(dp);
System.out.println(rcvd);
}
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
} public static void main(String[] args) {
for (int i = 0; i < 10; i++)
new ChatterClient(i).start();
} }
执行结果(每次结果稍不同):
客户端的结果:
ChatterClient starting
ChatterClient starting
Client #0, rcvd from /127.0.0.1, 8080: Echoed: ---Client #0, message #0---, from address: /127.0.0.1, port: 56388
Client #0, rcvd from /127.0.0.1, 8080: Echoed: ---Client #0, message #1---, from address: /127.0.0.1, port: 56388
Client #0, rcvd from /127.0.0.1, 8080: Echoed: ---Client #0, message #2---, from address: /127.0.0.1, port: 56388
Client #0, rcvd from /127.0.0.1, 8080: Echoed: ---Client #0, message #3---, from address: /127.0.0.1, port: 56388
ChatterClient starting
Client #1, rcvd from /127.0.0.1, 8080: Echoed: ---Client #1, message #0---, from address: /127.0.0.1, port: 56389
Client #0, rcvd from /127.0.0.1, 8080: Echoed: ---Client #0, message #4---, from address: /127.0.0.1, port: 56388
Client #1, rcvd from /127.0.0.1, 8080: Echoed: ---Client #1, message #1---, from address: /127.0.0.1, port: 56389
ChatterClient starting
Client #1, rcvd from /127.0.0.1, 8080: Echoed: ---Client #1, message #2---, from address: /127.0.0.1, port: 56389
Client #1, rcvd from /127.0.0.1, 8080: Echoed: ---Client #1, message #3---, from address: /127.0.0.1, port: 56389
ChatterClient starting
Client #1, rcvd from /127.0.0.1, 8080: Echoed: ---Client #1, message #4---, from address: /127.0.0.1, port: 56389
ChatterClient starting
ChatterClient starting
ChatterClient starting
Client #2, rcvd from /127.0.0.1, 8080: Echoed: ---Client #2, message #0---, from address: /127.0.0.1, port: 56390
Client #3, rcvd from /127.0.0.1, 8080: Echoed: ---Client #3, message #0---, from address: /127.0.0.1, port: 56391
ChatterClient starting
Client #2, rcvd from /127.0.0.1, 8080: Echoed: ---Client #2, message #1---, from address: /127.0.0.1, port: 56390
Client #3, rcvd from /127.0.0.1, 8080: Echoed: ---Client #3, message #1---, from address: /127.0.0.1, port: 56391
ChatterClient starting
Client #2, rcvd from /127.0.0.1, 8080: Echoed: ---Client #2, message #2---, from address: /127.0.0.1, port: 56390
Client #3, rcvd from /127.0.0.1, 8080: Echoed: ---Client #3, message #2---, from address: /127.0.0.1, port: 56391
Client #4, rcvd from /127.0.0.1, 8080: Echoed: ---Client #4, message #0---, from address: /127.0.0.1, port: 56392
Client #2, rcvd from /127.0.0.1, 8080: Echoed: ---Client #2, message #3---, from address: /127.0.0.1, port: 56390
Client #3, rcvd from /127.0.0.1, 8080: Echoed: ---Client #3, message #3---, from address: /127.0.0.1, port: 56391
Client #2, rcvd from /127.0.0.1, 8080: Echoed: ---Client #2, message #4---, from address: /127.0.0.1, port: 56390
Client #5, rcvd from /127.0.0.1, 8080: Echoed: ---Client #5, message #0---, from address: /127.0.0.1, port: 56393
Client #6, rcvd from /127.0.0.1, 8080: Echoed: ---Client #6, message #0---, from address: /127.0.0.1, port: 56394
Client #5, rcvd from /127.0.0.1, 8080: Echoed: ---Client #5, message #1---, from address: /127.0.0.1, port: 56393
Client #4, rcvd from /127.0.0.1, 8080: Echoed: ---Client #4, message #1---, from address: /127.0.0.1, port: 56392
Client #3, rcvd from /127.0.0.1, 8080: Echoed: ---Client #3, message #4---, from address: /127.0.0.1, port: 56391
Client #9, rcvd from /127.0.0.1, 8080: Echoed: ---Client #9, message #0---, from address: /127.0.0.1, port: 56397
Client #5, rcvd from /127.0.0.1, 8080: Echoed: ---Client #5, message #2---, from address: /127.0.0.1, port: 56393
Client #9, rcvd from /127.0.0.1, 8080: Echoed: ---Client #9, message #1---, from address: /127.0.0.1, port: 56397
Client #6, rcvd from /127.0.0.1, 8080: Echoed: ---Client #6, message #1---, from address: /127.0.0.1, port: 56394
Client #5, rcvd from /127.0.0.1, 8080: Echoed: ---Client #5, message #3---, from address: /127.0.0.1, port: 56393
Client #4, rcvd from /127.0.0.1, 8080: Echoed: ---Client #4, message #2---, from address: /127.0.0.1, port: 56392
Client #9, rcvd from /127.0.0.1, 8080: Echoed: ---Client #9, message #2---, from address: /127.0.0.1, port: 56397
Client #7, rcvd from /127.0.0.1, 8080: Echoed: ---Client #7, message #0---, from address: /127.0.0.1, port: 56395
Client #6, rcvd from /127.0.0.1, 8080: Echoed: ---Client #6, message #2---, from address: /127.0.0.1, port: 56394
Client #5, rcvd from /127.0.0.1, 8080: Echoed: ---Client #5, message #4---, from address: /127.0.0.1, port: 56393
Client #9, rcvd from /127.0.0.1, 8080: Echoed: ---Client #9, message #3---, from address: /127.0.0.1, port: 56397
Client #7, rcvd from /127.0.0.1, 8080: Echoed: ---Client #7, message #1---, from address: /127.0.0.1, port: 56395
Client #6, rcvd from /127.0.0.1, 8080: Echoed: ---Client #6, message #3---, from address: /127.0.0.1, port: 56394
Client #9, rcvd from /127.0.0.1, 8080: Echoed: ---Client #9, message #4---, from address: /127.0.0.1, port: 56397
Client #4, rcvd from /127.0.0.1, 8080: Echoed: ---Client #4, message #3---, from address: /127.0.0.1, port: 56392
Client #8, rcvd from /127.0.0.1, 8080: Echoed: ---Client #8, message #0---, from address: /127.0.0.1, port: 56396
Client #6, rcvd from /127.0.0.1, 8080: Echoed: ---Client #6, message #4---, from address: /127.0.0.1, port: 56394
Client #7, rcvd from /127.0.0.1, 8080: Echoed: ---Client #7, message #2---, from address: /127.0.0.1, port: 56395
Client #4, rcvd from /127.0.0.1, 8080: Echoed: ---Client #4, message #4---, from address: /127.0.0.1, port: 56392
Client #7, rcvd from /127.0.0.1, 8080: Echoed: ---Client #7, message #3---, from address: /127.0.0.1, port: 56395
Client #8, rcvd from /127.0.0.1, 8080: Echoed: ---Client #8, message #1---, from address: /127.0.0.1, port: 56396
Client #7, rcvd from /127.0.0.1, 8080: Echoed: ---Client #7, message #4---, from address: /127.0.0.1, port: 56395
Client #8, rcvd from /127.0.0.1, 8080: Echoed: ---Client #8, message #2---, from address: /127.0.0.1, port: 56396
Client #8, rcvd from /127.0.0.1, 8080: Echoed: ---Client #8, message #3---, from address: /127.0.0.1, port: 56396
Client #8, rcvd from /127.0.0.1, 8080: Echoed: ---Client #8, message #4---, from address: /127.0.0.1, port: 56396
服务器端结果:
Server started
---Client #0, message #0---, from address: /127.0.0.1, port: 56388
---Client #0, message #1---, from address: /127.0.0.1, port: 56388
---Client #0, message #2---, from address: /127.0.0.1, port: 56388
---Client #0, message #3---, from address: /127.0.0.1, port: 56388
---Client #1, message #0---, from address: /127.0.0.1, port: 56389
---Client #0, message #4---, from address: /127.0.0.1, port: 56388
---Client #1, message #1---, from address: /127.0.0.1, port: 56389
---Client #1, message #2---, from address: /127.0.0.1, port: 56389
---Client #1, message #3---, from address: /127.0.0.1, port: 56389
---Client #1, message #4---, from address: /127.0.0.1, port: 56389
---Client #4, message #0---, from address: /127.0.0.1, port: 56392
---Client #2, message #0---, from address: /127.0.0.1, port: 56390
---Client #3, message #0---, from address: /127.0.0.1, port: 56391
---Client #2, message #1---, from address: /127.0.0.1, port: 56390
---Client #3, message #1---, from address: /127.0.0.1, port: 56391
---Client #2, message #2---, from address: /127.0.0.1, port: 56390
---Client #3, message #2---, from address: /127.0.0.1, port: 56391
---Client #2, message #3---, from address: /127.0.0.1, port: 56390
---Client #3, message #3---, from address: /127.0.0.1, port: 56391
---Client #4, message #1---, from address: /127.0.0.1, port: 56392
---Client #2, message #4---, from address: /127.0.0.1, port: 56390
---Client #5, message #0---, from address: /127.0.0.1, port: 56393
---Client #6, message #0---, from address: /127.0.0.1, port: 56394
---Client #9, message #0---, from address: /127.0.0.1, port: 56397
---Client #3, message #4---, from address: /127.0.0.1, port: 56391
---Client #5, message #1---, from address: /127.0.0.1, port: 56393
---Client #5, message #2---, from address: /127.0.0.1, port: 56393
---Client #4, message #2---, from address: /127.0.0.1, port: 56392
---Client #9, message #1---, from address: /127.0.0.1, port: 56397
---Client #6, message #1---, from address: /127.0.0.1, port: 56394
---Client #5, message #3---, from address: /127.0.0.1, port: 56393
---Client #7, message #0---, from address: /127.0.0.1, port: 56395
---Client #9, message #2---, from address: /127.0.0.1, port: 56397
---Client #6, message #2---, from address: /127.0.0.1, port: 56394
---Client #5, message #4---, from address: /127.0.0.1, port: 56393
---Client #4, message #3---, from address: /127.0.0.1, port: 56392
---Client #9, message #3---, from address: /127.0.0.1, port: 56397
---Client #7, message #1---, from address: /127.0.0.1, port: 56395
---Client #8, message #0---, from address: /127.0.0.1, port: 56396
---Client #6, message #3---, from address: /127.0.0.1, port: 56394
---Client #9, message #4---, from address: /127.0.0.1, port: 56397
---Client #7, message #2---, from address: /127.0.0.1, port: 56395
---Client #6, message #4---, from address: /127.0.0.1, port: 56394
---Client #4, message #4---, from address: /127.0.0.1, port: 56392
---Client #7, message #3---, from address: /127.0.0.1, port: 56395
---Client #8, message #1---, from address: /127.0.0.1, port: 56396
---Client #7, message #4---, from address: /127.0.0.1, port: 56395
---Client #8, message #2---, from address: /127.0.0.1, port: 56396
---Client #8, message #3---, from address: /127.0.0.1, port: 56396
---Client #8, message #4---, from address: /127.0.0.1, port: 56396
think in java 读书笔记 3 —— 数据报的更多相关文章
- think in java 读书笔记 2 —— 套接字
目录 think in java 读书笔记 1 ——移位 think in java 读书笔记 2 —— 套接字 think in java 读书笔记 3 —— 数据报 概要 1. 套接字基本知识 2 ...
- think in java 读书笔记 1 ——移位
目录 think in java 读书笔记 1 ——移位 think in java 读书笔记 2 —— 套接字 think in java 读书笔记 3 —— 数据报 在Think in Java中 ...
- Thinking In Java读书笔记--对象导论
Thinking In Java读书笔记--对象导论[对象]服务提供者==>将对象看做一个服务提供者[程序员分类][类创造者]/[客户端程序员] [访问控制存在的原因?][1]客户端程序员无法触 ...
- head first java读书笔记
head first java读书笔记 1. 基本信息 页数:689 阅读起止日期:20170104-20170215 2. 标签 Java入门 3. 价值 8分 4. 主题 使用面向对象的思路介绍J ...
- Java读书笔记1
Java逍遥游记读书笔记 前言 必须先来一句,这是入门级别,高手勿喷~ 写Android的时候总有一些语句不是很理解,其实大部分是Java的内容,所以想系统的学下Java. 这本书——<Java ...
- java读书笔记二
这是我的一些读书笔记: 我研究了一下面向对象: 面向对象符合人类看待事物的一般规律,对象的方法的实现细节是包装的,只有对象方法的实现者了解细节 我觉得面向过程是由过程.步骤.函数组成,过程是核心,面向 ...
- Effective Java读书笔记完结啦
Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...
- Effective java读书笔记
2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...
- 【java读书笔记】——java开篇宏观把控 + HelloWorld
学完java有一段时间了,一直没有做对应的总结,总认为有一种缺憾.从这篇博客開始,将自己平时的学习笔记进行总结归纳,分享给大家. 这篇博客主要简单的介绍一下java的基础知识,基本的目的是扫盲.原来仅 ...
随机推荐
- 如何选择正确的DevOps工具
坦白的讲:世界上没有哪种工具能够像DevOps这么神奇(或敏捷,或精益).DevOps在开发和运营团队之间建立了完美的合作与沟通,因此与其说这是一种神奇的工具,不如说是一种文化的转变. 然而,团队之间 ...
- R 给data.frame(dataframe)添加一列
x<-data.frame(apple=c(1,4,2,3),pear=c(4,8,5,2)) x # apple pear # 1 1 4 # 2 4 8 # 3 2 5 # 4 3 2 x$ ...
- nessus网页报错: Scans can not be saved without a policy. Please create a policy before proce
Policies添加一个用户就好了暂时还不懂什么意思以后知道再补..............
- 10.Properties
The common language runtime (CLR) offers two kinds of properties: 1.parameterless properties, which ...
- JAVA运算符和优先级
1.算术运算符: ++ 和 -- 既可以出现在操作数的左边,也可以出现在右边,但结果是不同,如: ①int a=5: int b=a++: #先把a赋给b,a再自增 ②int a=5: int b=+ ...
- 移动h5自适应布局
问题一,分辨率Resolution适配:不同屏幕宽度,html元素宽高比和字体大小,元素之间的距离自适应,使用rem单位. 问题二,单位英寸像素数PPI适配:使用rem单位,文字会发虚.段落文字,使用 ...
- 详解 ASP.NET异步
在前文中,介绍了.NET下的多种异步的形式,在WEB程序中,天生就是多线程的,因此使用异步应该更为谨慎.本文将着重展开ASP.NET中的异步. [注意]本文中提到的异步指的是服务器端异步,而非客户端异 ...
- php 上传文件。$_FILES
<form name="article" method="post" enctype="multipart/form-data" ac ...
- Yii2文件上传
首先在app\controllers下建立TestController.php,内容为如下代码: <?php namespace app\controllers; use Yii; use yi ...
- Python学习(18)面向对象
目录 Python 面向对象 创建实例对象 Python内置类属性 Pyyhon对象销毁(垃圾回收) 类属性与方法 Python 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此 ...