一、TCP/IP簡介
TCP/IP協議族是互連網使用的協議,也可以用在獨立的專用網路中。
TCP/IP協議族包括了IP協議、TCP協議和UDP協議。
IP協議使用IP地址來分發報文,但它是儘力而為的服務,報文可能丟失、亂序或者
重複發送。TCP和UDP協議在IP協議基礎上增加了連接埠號碼,從而在兩台主機的應用
程式間建立起透明的串連。
不同的是,TCP協議會對IP層的錯誤進行修複,它通過握手訊息在主機間建立串連,
之後通過在訊息中加入序號來恢複訊息中的錯誤。而UDP只是簡單地擴充了IP協議,
使它能夠在應用程式之間工作,而不是主機之間。
關於IP地址,一台主機可以有多個網路介面,而一個介面又可以有多個地址。
有些IP地址是有特殊用途的:
A.迴環地址:127.0.0.1,總是被分配給一個迴環介面,主要用於測試。
B.私人地址:以10、192.168、172.(16-31)開頭,用於私人網路。NAT裝置轉寄報文
時,將一個介面中報文的私人地址連接埠對映射成另一個介面中的公有地址連接埠對。這
就使一小組主機能夠共用一個IP地址對。
C.多播地址:第一個數字在224~239之間。
二、Socket基礎
1.地址的獲得
public static void main(String[] args) { try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface iface = interfaces.nextElement(); System.out.println("Interface: " + iface.getName()); Enumeration<InetAddress> addrList = iface.getInetAddresses(); if (!addrList.hasMoreElements()) System.out.println("No address"); while (addrList.hasMoreElements()) { InetAddress address = addrList.nextElement(); System.out.println("Address: " + address.getHostAddress()); } } } catch (SocketException e) { e.printStackTrace(); } }
2.TCP執行個體程式
要注意一點,雖然在Client端只用了一個write()方法發送字串,伺服器端也可能從
多個塊中接受該資訊。即使回饋字串在伺服器返回時存於一個塊中,也可能被TCP
協議分割成多個部分。
TCPEchoClientTest.java
public static void main(String[] args) throws IOException { String server = args[0]; byte[] data = args[1].getBytes(); int port = 7; Socket socket = new Socket(server, port); System.out.println("Connected to server..."); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); out.write(data); int totalBytesRcvd = 0; int bytesRcvd; while (totalBytesRcvd < data.length) { if ((bytesRcvd = in.read(data, totalBytesRcvd, data.length - totalBytesRcvd)) == -1) throw new SocketException("Connection closed"); totalBytesRcvd += bytesRcvd; } System.out.println("Received: " + new String(data)); socket.close(); }
TCPEchoServerTest.java
private static final int BUFSIZE = 32; public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(7); int recvMsgSize; byte[] receiveBuf = new byte[BUFSIZE]; while (true) { Socket socket = serverSocket.accept(); System.out.println("Handling client " + " from remote " + socket.getRemoteSocketAddress() + " at local " + socket.getLocalSocketAddress()); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } socket.close(); } }
注意new Socket時指定的是遠端伺服器監聽的連接埠號碼而沒有指定本地連接埠,因此將
採用預設地址和可用的連接埠號碼。在我的機器上Client連接埠是4593,串連到伺服器的
連接埠7。
3.UDP執行個體程式
為什麼使用UDP協議?如果應用程式只交換少量的資料,TCP串連的建立階段就至少
要傳輸其兩倍的資訊量(還有兩倍的往返時間)。
UDPEchoClientTest.java
public static void main(String[] args) throws IOException { InetAddress serverAddress = InetAddress.getByName(args[0]); byte[] bytesToSend = args[1].getBytes(); DatagramSocket socket = new DatagramSocket(); socket.setSoTimeout(3000); DatagramPacket sendPacket = new DatagramPacket( bytesToSend, bytesToSend.length, serverAddress, 7); DatagramPacket receivePacket = new DatagramPacket( new byte[bytesToSend.length], bytesToSend.length); // Packets may be lost, so we have to keep trying int tries = 0; boolean receivedResponse = false; do { socket.send(sendPacket); try { socket.receive(receivePacket); if (!receivePacket.getAddress().equals(serverAddress)) throw new IOException("Receive from unknown source"); receivedResponse = true; } catch (IOException e) { tries++; System.out.println("Timeout, try again"); } } while (!receivedResponse && tries < 5); if (receivedResponse) System.out.println("Received: " + new String(receivePacket.getData())); else System.out.println("No response"); socket.close(); }
UDPEchoServerTest.java
private static final int ECHOMAX = 255; public static void main(String[] args) throws IOException { DatagramSocket socket = new DatagramSocket(7); DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX); while (true) { socket.receive(packet); System.out.println("Handling client at " + packet.getAddress()); socket.send(packet); packet.setLength(ECHOMAX); } }
通過這個例子與之前TCP的執行個體進行比較,有如下區別:
A.DatagramSocket在建立時不需要指定目的地址,因為UDP不需要建立串連,每個
資料報文都可以發送或接收於不同的目的地址。
B.如果像TCP一樣在read()上阻塞等待,將可能永遠阻塞在那裡,因為UDP協議只是
簡單地擴充了IP協議,UDP報文可能丟失掉。所以一定要設定阻塞等待的逾時時間。
C.UDP協議保留了訊息的邊界資訊,每次receive()調用最多隻能接收一次send()方法
調用所發送的資料。
D.一個UDP報文DatagramPacket能傳輸的最大資料是65507位元組,超出部分的位元組將
自動被丟棄,而且對接收程式也沒有任何的提示。因此緩衝數組可以設定成65000位元組
左右是安全的。
E.如果反覆使用同一個DatagramPacket執行個體調用receive()方法,每次調用前都必須顯式
地將訊息的內部長度重設為緩衝區的實際長度。