java 網路程式設計 第 8 章 用 java 實作 以 udp 協定為主的應用. 認識 udp 協定...

23
Java 網網網網網網 第 8 第 第 Java 第第 第 UDP 第第第第第第第

Upload: pamela-bailey

Post on 13-Dec-2015

233 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

Java 網路程式設計

第 8 章 用 Java 實作以 UDP 協定為主的應用

Page 2: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

認識 UDP 協定

UDP(user datagram protocol) 協定可以讓應用程式在不需要建立連線(connection) 的情況下送出封裝的 IP 封包 (encapsulated IP datagrams)

有關於 UDP 的資訊可以查詢 RFC 768的文件

UDP 傳送的片段 (segments) 包括 8 個bytes 的 header 與隨後的 payload

Page 3: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

UDP header 的格式

Page 4: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

通訊埠 (port) 的分配與使用

Page 5: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

以 UDP 為基礎的幾個網際網路協定

BootP(Boot Protocol) DHCP(dynamic host configuration

protocol)SNMP(simple network management

protocol)TFTP(trivial file transfer protocol)DNS(domain name system)

Page 6: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

在 IP 封包中和協定有關的資料

Page 7: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

各種網際網路協定使用的 port number

協定 Port

number

Transport協定

涵義

BootP 67 U DP BOOTstrap協定(server)

BootP 68 U DP BOOTstrap協定(client)

DH C P 67 U DP DH C P 協定(server)

DH C P 68 U DP DH C P 協定(client)

DN S 53 U DP/TC P Domain name system

FTP 21 TC P Server/control

FTP 20 TC P Server/data

H TTP 80 TC P/U DP H TTP/server

N etBT 138 U DP N etBIOS datagram

N etBT 139 TC P N etBIOS session

SM TP 25 TC P SM TP/server

SN M P 161 U DP SN M P/server

SN M P 162 U DP SN M P/trap manager

Telnet 23 TC P 遠端登入

TFTP 69 U DP 簡易(trivial)FTP

WIN S 137 U DP Windows Internet N ame Service

Page 8: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

IP 協定的 protocol type value

協定 10進位 16進位 說明

EGP 8 8 Exterior Gateway Protocol

IC M P 1 1 Internet C ontrol M essage Protocol

IGM P 2 2 Internet G roup M anagement Protocol

IGRP 88 58 Internet Gateway Routing Protocol

OSPF 89 59 Open Shortest Path First

TC P 6 6 Transmission C ontrol Protocol

U DP 17 11 U ser Datagram Protocol

Page 9: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

Client 的定義與觀點

The client-server paradigm uses the direction of initiation to categorize whether a

program is a client or server. In general, an application that initiates peer-to-peer

communication is called a client. End users usually invoke client software when

they use a network service.

Page 10: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

Server 的定義與觀點

Each time a client application executes, it contacts a server, sends a request, and

awaits a response. A server is any program that waits for incoming communication

requests from a client. The server receives a client’s request, performs the

necessary computation, and returns the result to the client.

Page 11: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

通訊軟體設計的觀點

多重協定的伺服程式 (multiprotocol servers)

多重服務的伺服程式 (multiservice servers)

Page 12: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

multiprotocol server 的架構

Page 13: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

connection-oriented multiservice server 的架構

Page 14: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

concurrent, connection-oriented multiservice server 的架構

Page 15: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

Java 對於 UDP 的支援

java.net.DatagramPacket 類別java.net.DatagramSocket 類別

Page 16: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

建立在 UDP 上的應用實作

用 UDP 實作 Echo Protocol 另外一種 Echo Service 的設計實作 DNS

Page 17: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

RFC 文件

縮寫 協定名稱 RFC 文件號碼

ARP Address resolution 826

DH C P Dynamic host configuration 1541

DN S Domain name system 1034, 1035

FTP File transfer 959

IC M P Internet control message 792

IP Internet 791

N BT N etBIOS over TC P 1001,1002

TC P Transmission control protocol 793

Telnet Teletype network service 854

U DP U ser datagram protocol 768

Page 18: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

UDP Server 程式 // UDP Server 程式 import java.io.*; import java.net.*; public class Server { public static void main(String args[]) throws Exception { byte buffer[]=new byte[10]; String msg; int portNo=5555; System.out.println("Server 端開始接受連線請求 !"); for (;;) { DatagramPacket packet = new DatagramPacket(buffer,buffer.length); DatagramSocket socket=new DatagramSocket(portNo); socket.receive(packet); msg=new String(buffer,0,packet.getLength()); System.out.println(" 收到下面的訊息 : " + msg); socket.close(); } } }

Page 19: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

UDP 客戶端的程式 // UDP 客戶端的程式 import java.io.*; import java.net.*; public class Client { public static void main(String args[]) throws Exception { int portNo =5555; System.out.print("Please input the IP address of destination :"); BufferedReader uip= new BufferedReader(new InputStreamReader(System.in)); String ServerIP=uip.readLine(); InetAddress addr=InetAddress.getByName(ServerIP); while (true) { System.out.print("輸入送出的訊息 :"); String msg=uip.readLine(); int oLength=msg.length(); byte buffer[]=new byte[oLength]; buffer=msg.getBytes(); DatagramPacket packet= new DatagramPacket(buffer,oLength,addr,portNo); DatagramSocket socket=new DatagramSocket(); socket.send(packet); socket.close(); } } }

Page 20: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

EchoUDPServer .java import java.net.*; import java.io.*; public class EchoUDPServer extends Thread { public static int SPORT = 7; // standard UDP port=7 public static int checkTime = 5000; // 預設的termination check time (以milliseconds為單位) // 最大的UDP資料長度 , 單位為bytes public static int maxUDP = 65508; // maximum UDP data length protected DatagramSocket socket; protected boolean onForService; public EchoUDPServer(int port) throws SocketException { super("EchoServer(" + port + ")"); socket = new DatagramSocket(port); socket.setSoTimeout(checkTime); onForService = true; start(); } public void ending() { onForService = false; } public void run() { System.out.println("Echo server使用以下的port : " + socket.getLocalPort()); DatagramPacket inputPacket = new DatagramPacket(new byte[maxUDP],maxUDP); while(onForService) { try { inputPacket.setLength(maxUDP); socket.receive(inputPacket); System.out.println("Echo server有收到資料! "); /* System.out.println("Echo server收到的資料 : "+ new String(inputPacket.getData())); */ socket.send(inputPacket); // 收到的資料再轉送出去(echo) } catch (InterruptedIOException e) { } catch (Throwable e) { } } try { socket.close(); } catch (Throwable e) { } } public static void main(String[] args) { if (args.length > 1) { System.err.println("使用方式 : EchoUDPServer { <port> }"); System.exit(1); } int port = SPORT; if (args.length == 1) { try { port = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.err.println("無效的port number : " + args[0]); System.exit(1); } } try { EchoUDPServer server = new EchoUDPServer(port); server.join(); } catch (Throwable e) { System.err.println("EchoUDPServer: " + e); System.exit(1); } } }

Page 21: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

EchoUDPClient.java import java.net.*; import java.io.*; public class EchoUDPClient { // 依據RFC 862 , UDP echo service的標準使用port number =7 public static final int SPORT = 7; // 嘗試接收echo server回應的次數 public static final int try_times = 3; // 嘗試的次數 public static final int etime = 1500; // 判定過時的期限, 以milliseconds為單位 // Maximum UDP payload size public static final int bsize = 508; // UDP buffer上限 // 為了產生單一echo請求的計數器 protected static int nextRequestID= 1250; // 避免instantiation private EchoUDPClient() { } // 假如主機有在指定的port上執行UDP echo service, 而且能傳回測試 // 的訊息, 則return true // 每隔一段時間做幾次的檢查 // ____________________________________________________________________ echo method public static boolean echo(InetAddress address,int port,int tries, int eheckExpire) throws IOException, SecurityException { if (address == null) throw new NullPointerException("echo server address的參數為空值"); // 產生唯一的request ID long outputReqID; synchronized(EchoUDPClient.class) { outputReqID = nextRequestID; System.out.println("outputReqID= "+outputReqID); nextRequestID++; } // 產生request packet payload (把資料寫入byte array) ByteArrayOutputStream OutputInByte = new ByteArrayOutputStream(); DataOutputStream Outputdata = new DataOutputStream(OutputInByte); try { Outputdata.writeLong(outputReqID); // 注意outputReqID是怎麼來的 Outputdata.close(); } catch (IOException e) { throw new RuntimeException("未預期的I/O例外狀況 : " + e); } // 由request來建立datagram packet DatagramPacket outputPacket = new DatagramPacket(OutputInByte.toByteArray(), OutputInByte.size(), address, port); DatagramPacket inputPacket = new DatagramPacket (new byte[bsize], bsize); DatagramSocket socket = new DatagramSocket(); socket.setSoTimeout(eheckExpire); try { for(int i=0; i<tries; i++) { /* System.out.println("Echo client送出的資料 : "+ new String(outputPacket.getData())); */ // 注意資料的處理 socket.send(outputPacket); try { socket.receive(inputPacket); // 等待接收資料 } catch (InterruptedIOException e) { continue; // 再嘗試 } catch (IOException e) { continue; } /* System.out.println("Echo client收到的資料 : "+inputPacket.getData()); */ // 將收到得byte-array轉成Java Unicode string ByteArrayInputStream byteArrayIn = new ByteArrayInputStream(inputPacket.getData(), 0, inputPacket.getLength()); DataInputStream dataInput = new DataInputStream(byteArrayIn); try { long reqID = dataInput.readLong(); System.out.println("client received : "+reqID); // client收到的 server echo if (outputReqID == reqID) { return(true); } } catch (IOException e) { } inputPacket.setLength(bsize); } } finally { try { socket.close(); } catch (Throwable e) { } } return(false); } // 主程式進入點___________________________________________ public static void main(String[] args) { if ( (args.length == 0) || (args.length > 2) ) { System.err.println("使用方式 : EchoUDPClient <host> {<port>}"); System.exit(1); } InetAddress address = null; try { address = InetAddress.getByName(args[0]); System.out.println("From client : "+address); } catch (UnknownHostException e) { System.err.println("無法找到主機 : " + args[0]); System.exit(1); } int port = SPORT; if (args.length > 1) { try { port = Integer.parseInt(args[1]); } catch (NumberFormatException e) { System.err.println("port number無效 " + args[1]); System.exit(1); } } try { if (echo(address, port, try_times , etime)) { System.out.println("以下的位址有提供echo service : " + address); } else { System.out.println("以下的位址未能echo : " + address); } System.exit(0); } catch (Throwable e) { System.err.println("EchoUDPClient: " + e); System.exit(1); } } // main方法結束 }

Page 22: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

MultiEcho.java import java.net.*; import java.io.*; class SimpleEchoServer extends Thread { private DatagramSocket ssock = null; SimpleEchoServer(int port) { try { ssock = new DatagramSocket(port); } catch (SocketException e) { e.printStackTrace(); } } public void run() { if (ssock == null) return; byte[] inbuf = new byte[24]; DatagramPacket request = new DatagramPacket(inbuf, inbuf.length); try { while (true) { // 準備等待連線接收資料 System.out.println("(server) Using port number " + ssock.getLocalPort()); System.out.println("(server) Using address " + ssock.getLocalAddress()); ssock.receive(request); System.out.println("server收到的資料 : "+ new String(request.getData())); // 傳回送出的資料 System.out.println("server送出去的資料 : "+ new String(request.getData())); ssock.send(request); } } catch (IOException e) { e.printStackTrace(); } } } class MultiEcho { static int echo_port = 7; // echo method在此____________________________________ (這是client端的動作) public static void echo(String msg, InetAddress dst, int port) { byte[] inbuf = new byte[24]; byte[] outbuf = msg.getBytes(); try { // DatagramSocket csock = new DatagramSocket(7); // 試試看有何結果! DatagramSocket csock = new DatagramSocket(); DatagramPacket request = new DatagramPacket(outbuf, outbuf.length, dst, port); DatagramPacket reply = new DatagramPacket(inbuf, inbuf.length); csock.send(request); System.out.println("client送出去的資料 : "+ new String(request.getData())); // 注意資料的處理方式 csock.receive(reply); System.out.println("client port number " + csock.getLocalPort()); System.out.println("client host address " + csock.getLocalAddress()); csock.close(); System.out.println("client收到的資料 : "+ new String(reply.getData())); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // 主程式進入點 __________________________________________ public static void main(String[] args) { if (args.length != 1) { System.err.println("使用方式 : java MultiEcho <message>"); System.exit(1); } // 啟動echo server SimpleEchoServer srv = new SimpleEchoServer(echo_port); // 呼叫建構子 // srv.setDaemon(true); // 設定成background daemon process srv.start(); try { // 這一段可以產生一個echo client // InetAddress dst = InetAddress.getLocalHost(); // 可以試試! InetAddress dst = InetAddress.getByName("localhost"); System.out.println(dst); echo(args[0], dst, echo_port); // 呼叫echo } catch (UnknownHostException e) { System.err.println("主機不明 : " + e); } } }

Page 23: Java 網路程式設計 第 8 章 用 Java 實作 以 UDP 協定為主的應用. 認識 UDP 協定 UDP(user datagram protocol) 協定可以讓 應用程式在不需要建立連線

UDPClient.java import java.io.*; import java.net.*; // 本類別建立一個UDP datagram client的基本架構 public class UDPClient { public final static boolean Debug = true; public final static int MAX_DGRAM_SIZE = 512, //收到的datagram的上限 NUM_RETRIES = 3; //重送的次數 protected DatagramSocket sock; // 用來接收與傳送資料 protected DatagramPacket outPacket, inPacket; protected byte[] outBuffer, inBuffer; protected int outBufferLen, inBufferLen; protected int outPort, inPort; protected InetAddress remoteAddress; protected int RTimer = 2400, //以milliseconds為單位 retry = 0; //重試的次數 protected DatagramReplyT replySlave; protected DatagramSendT sendSlave; // 建構子 // inBufSize: 收到的data buffer的大小 // outBuf: 傳送的資料 public UDPClient(int inBufSize,byte[] outBuf, int localPort, int remotePort, InetAddress remoteAddress, int waiting ) { outPort = remotePort; inPort = localPort; outBuffer = outBuf; outBufferLen = outBuf.length; inBufferLen = ((inBufSize > 0) ? inBufSize: MAX_DGRAM_SIZE); inBuffer = new byte[inBufferLen]; remoteAddress = remoteAddress; RTimer = ((waiting > 0) ? waiting: RTimer); inPacket = new DatagramPacket(inBuffer, inBufferLen); outPacket = new DatagramPacket(outBuffer, outBufferLen, remoteAddress, outPort); } // output buffer未定時使用的建構子 public UDPClient(int inBufSize,int localPort, int remotePort, InetAddress remoteAddress,int waiting ) { outPort = remotePort; inPort = localPort; inBufferLen = ((inBufSize > 0) ? inBufSize: MAX_DGRAM_SIZE); inBuffer = new byte[inBufferLen]; remoteAddress = remoteAddress; RTimer = ((waiting > 0) ? waiting: RTimer); inPacket = new DatagramPacket(inBuffer, inBufferLen); } public void setOutputBuf(byte[] outBuf) { outBuffer = outBuf; outBufferLen = outBuf.length; outPacket = new DatagramPacket(outBuffer, outBufferLen, remoteAddress, outPort); } public DatagramPacket procTrans() throws SocketException, IOException { retry = 0; Bind(); sendSlave = new DatagramSendT(outPacket, sock,RTimer); replySlave = new DatagramReplyT(inPacket, sock,NUM_RETRIES * RTimer); sendSlave.start();// 開始傳送datagrams replySlave.start();//開始等待接收datagrams int receivingState = replySlave.waitOnReply();//在此阻絕 if (Debug) System.out.println("UDPClient receivingState: " + receivingState); if (receivingState != 1) inPacket = null; sendSlave.outState = 1;//停止sender thread // if (replySlave.isAlive()) // deprecated method // try {replySlave.stop(); } catch (Throwable st) {} return inPacket; } protected void Bind() throws SocketException { if (inPort != 0) sock = new DatagramSocket(inPort); else sock = new DatagramSocket(); if (Debug) System.out.println("通訊埠 : " + sock.getLocalPort()); } public void Dispose() // { if (sock != null) sock.close(); inPacket = null; outPacket = null; outBuffer = null; inBuffer = null; } } // 注意此類別的功能 class DatagramReplyT extends Thread { private final static boolean Debug = false; DatagramPacket sinPacket; DatagramSocket sinSocket; UDPClient dParent; int fwaiting = 30000; public int receivedState = 0; public DatagramReplyT(DatagramPacket recvPacket, DatagramSocket recvSocket, int totalwaiting) { sinPacket = recvPacket; sinSocket = recvSocket; fwaiting = totalwaiting; } public synchronized int waitOnReply() { try {wait(fwaiting);} catch (InterruptedException intEx) { if (Debug) System.out.println("waitOnReply interrupt!"); } return receivedState; } public synchronized void run() { if (Debug) System.out.println("DatagramReplyT running..."); try { if (Debug) System.out.println("DatagramReplyT receiving..."); sinSocket.receive(sinPacket); receivedState = 1; //接收成功 System.out.println("DatagramReplyT received OK!"); } catch (IOException receiveEx) { System.err.println("receive failed! " + receiveEx); receivedState = -1; }; notify(); } } class DatagramSendT extends Thread { protected final static boolean Debug = false; DatagramPacket soutPacket; DatagramSocket soutSocket; public int outState = 0; //沒有收到但也沒失敗 protected int retryInterval; //以milliseconds為單位 public DatagramSendT(DatagramPacket sendPacket, DatagramSocket sendSocket,int retryTime) { soutPacket = sendPacket; soutSocket = sendSocket; retryInterval = retryTime; } public void run() { if (Debug) System.out.println("DatagramSendT running..."); while (outState == 0) { try { if (Debug) System.out.println("DatagramSendT sending..."); soutSocket.send(soutPacket); outState = 0; if (Debug) System.out.println("DatagramSendT sent ok!"); try {this.sleep(retryInterval);} catch (InterruptedException iEx) { if (Debug) System.err.println("DatagramSendT sleep interrupted!"); outState = -1; }; } catch (IOException sendException) { if (Debug) System.err.println("傳送失敗! " + sendException); outState = -1; }; } } }