zoukankan      html  css  js  c++  java
  • think in java 读书笔记 3 —— 数据报

     目录

    think in java 读书笔记 1 ——移位

    think in java 读书笔记 2 —— 套接字

    think in java 读书笔记 3 —— 数据报

    概要

    1. 数据报基本知识

    2. 服务器端和客户端程序实例

    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:

     1 package com.xingle_test.datagram;
     2 
     3 import java.net.DatagramPacket;
     4 import java.net.InetAddress;
     5 
     6 /**
     7  * @ClassName: Dgram
     8  * @Description: 从一个String 里创建DatagramPacket 的工作(或者从DatagramPacket 里创建String)
     9  * @author xingle
    10  * @date 2014年7月25日 下午11:05:03
    11  */
    12 public class Dgram {
    13     public static DatagramPacket toDatagram(String s, InetAddress destIA,
    14             int destPort) {
    15 
    16         byte[] buf = s.getBytes();
    17         return new DatagramPacket(buf, buf.length, destIA, destPort);
    18     }
    19 
    20     public static String toString(DatagramPacket p) {
    21 
    22         return new String(p.getData(), 0, p.getLength());
    23     }
    24 }

    数据报演示的服务器代码:

     1 package com.xingle_test.datagram;
     2 
     3 import java.io.IOException;
     4 import java.net.DatagramPacket;
     5 import java.net.DatagramSocket;
     6 
     7 import java.net.SocketException;
     8 
     9 /**
    10  * 数据报 服务器端
    11  * 
    12  * @ClassName: ChatterServer 
    13  * @author Xingle
    14  * @date 2014-7-23 下午3:42:09
    15  */
    16 public class ChatterServer {
    17  
    18     static final int INPORT = 8080;
    19     private byte[] buf = new byte[1000];
    20     private DatagramPacket dp = new DatagramPacket(buf, buf.length);
    21     // Can listen & send on the same socket:
    22     private DatagramSocket socket;
    23 
    24     public ChatterServer() {
    25         try {
    26             socket = new DatagramSocket(INPORT);
    27             System.out.println("Server started");
    28             while (true) {
    29                 // Block until a datagram appears:
    30                 socket.receive(dp);
    31                 String rcvd = Dgram.toString(dp) + ", from address: "
    32                         + dp.getAddress() + ", port: " + dp.getPort();
    33                 System.out.println(rcvd);
    34                 String echoString = "Echoed: " + rcvd;
    35                 // Extract the address and port from the
    36                 // received datagram to find out where to
    37                 // send it back:
    38                 DatagramPacket echo = Dgram.toDatagram(echoString,
    39                         dp.getAddress(), dp.getPort());
    40                 socket.send(echo);
    41             }
    42         } catch (SocketException e) {
    43             System.err.println("Can't open socket");
    44             System.exit(1);
    45         } catch (IOException e) {
    46             System.err.println("Communication error");
    47             e.printStackTrace();
    48         }
    49     }
    50 
    51     public static void main(String[] args) {
    52         new ChatterServer();
    53     }
    54 
    55 }

    ChatterServer 创建了一个用来接收消息的DatagramSocket(数据报套接字),而不是在我们每次准备接收一条新消息时都新建一个。这个单一的DatagramSocket 可以重复使用。它有一个端口号,因为这属于服务器,客户必须确切知道自己把数据报发到哪个地址。尽管有一个端口号,但没有为它分配因特网地址,因为它就驻留在“这”台机器内,所以知道自己的因特网地址是什么(目前是默认的localhost)。在无限while循环中,套接字被告知接收数据(receive())。然后暂时挂起,直到一个数据报出现,再把它反馈回我们希望的接收人——DatagramPacket dp——里面。数据包(Packet)会被转换成一个字串,同时插入的还有数据包的起源因特网地址及套接字。这些信息会显示出来,然后添加一个额外的字串,指出自己已从服务器反馈回来了。

      为了将一条消息送回它真正的始发客户,需要知道那个客户的因特网地址以及端口号。幸运的是,所有这些资料均已非常周到地封装到发出消息的DatagramPacket 内部,所以我们要做的全部事情就是用getAddress()和getPort()把它们取出来。利用这些资料,可以构建DatagramPacket echo——它通过与接收用的相同的套接字发送回来。除此以外,一旦套接字发出数据报,就会添加“这”台机器的因特网地址及端口信息,所以当客户接收消息时,它可以利用getAddress()和getPort()了解数据报来自何处。事实上,getAddress()和getPort()唯一不能告诉我们数据报来自何处的前提是:我们创建一个待发送的数据报,并在正式发出之前调用了getAddress()和getPort()。到数据报正式发送的时候,这台机器的地址以及端口才会写入数据报。所以我们得到了运用数据报时一项重要的原则:不必跟踪一条消息的来源地!因为它肯定保存在数据报里。

      为测试服务器的运转是否正常,下面这程序将创建大量客户(线程),它们都会将数据报包发给服务器,并等候服务器把它们原样反馈回来。

    客户端:

     1 package com.xingle_test.datagram;
     2 
     3 import java.io.IOException;
     4 import java.net.DatagramPacket;
     5 import java.net.DatagramSocket;
     6 import java.net.InetAddress;
     7 import java.net.SocketException;
     8 import java.net.UnknownHostException;
     9 
    10 /**
    11  * 客户端(线程)
    12  * @ClassName: ChatterClient 
    13  * @author Xingle
    14  * @date 2014-7-25 下午5:22:52
    15  */
    16 public class ChatterClient extends Thread {
    17 
    18     private DatagramSocket s;
    19     private InetAddress hostAddress;
    20     private byte[] buf = new byte[1000];
    21     private DatagramPacket dp = new DatagramPacket(buf, buf.length);
    22     private int id;
    23 
    24     public ChatterClient(int identifier) {
    25         id = identifier;
    26         try {
    27             // Auto-assign port number:
    28             s = new DatagramSocket();
    29             hostAddress = InetAddress.getByName("localhost");
    30         } catch (UnknownHostException e) {
    31             System.err.println("Cannot find host");
    32             System.exit(1);
    33         } catch (SocketException e) {
    34             System.err.println("Can't open socket");
    35             e.printStackTrace();
    36             System.exit(1);
    37         }
    38         System.out.println("ChatterClient starting");
    39     }
    40 
    41     public void run() {
    42         try {
    43             for (int i = 0; i < 5; i++) {
    44                 String outMessage = "---Client #" + id + ", message #" + i+"---";
    45                 // Make and send a datagram:
    46                 s.send(Dgram.toDatagram(outMessage, hostAddress,
    47                         ChatterServer.INPORT));
    48                 // Block until it echoes back:
    49                 s.receive(dp);
    50                 // Print out the echoed contents:
    51                 String rcvd = "Client #" + id + ", rcvd from "
    52                         + dp.getAddress() + ", " + dp.getPort() + ": "
    53                         + Dgram.toString(dp);
    54                 System.out.println(rcvd);
    55             }
    56         } catch (IOException e) {
    57             e.printStackTrace();
    58             System.exit(1);
    59         }
    60     }
    61 
    62     public static void main(String[] args) {
    63         for (int i = 0; i < 10; i++)
    64             new ChatterClient(i).start();
    65     }
    66 
    67 }

    执行结果(每次结果稍不同):

    客户端的结果:

    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

  • 相关阅读:
    go开发环境配置
    Go环境配置
    为什么Redis集群有16384个槽【转发】
    Spring Cloud Hystrix降级处理超时时间设置
    win10下查看进程,杀死进程【转载】
    SpringCloud的各种超时时间配置效果
    解决springcloud Feign项目中遇到的timeout请求超时的问题【转载】
    数据库索引
    XML
    JDBC
  • 原文地址:https://www.cnblogs.com/xingele0917/p/3865231.html
Copyright © 2011-2022 走看看