網絡協議
IP協議
IP是Internet Protocol的簡稱,是一種網絡協議。Internet 網絡采用的協議是TCP/IP協議,其全稱是Transmission Control Protocol/Internet Protocol。Internet 依靠TCP/IP協議,在全球范圍內實現了不同硬件結構、不同操作系統、不同網絡系統間的互聯。在Internet 網絡上存在著數以億計的主機,每臺主機都用網絡為其分配的 Internet 地址代表自己,這個地址就是I地址。到目前為止,I地址用4個字節,也就是32位的二進制數來表示,稱為IPv4。為了便于使用,通常取用每個字節的十進制數,并且每個字節之間用圓點隔開來表示I地址,如192.168.1.1。現在人們正在試驗使用16個字節來表示I地址,這就是IPv6,但IPv6還沒有投入使用。
TCP/IP 模式是一種層次結構,共分為4層,分別為應用層、傳輸層、互聯網層和網絡層。各層實現特定的功能,提供特定的服務和訪問接口,并具有相對的獨立性
?TCP與UDP協議
在TCPAIP 協議棧中,有兩個高級協議是網絡應用程序編寫者應該了解的,即傳輸控制協議(Transmission Control Protocol,TCP)與用戶數據報協議(User Datagram Protocol, UDP)。
TCP 協議是一種以固接連線為基礎的協議,它提供兩臺計算機間可靠的數據傳送。TCP可以保證數據從一端送至連接的另一端時,能夠確實送達,而且抵達的數據的排列順序和送出時的順序相同。因此,TCP協議適合可靠性要求比較高的場合。就像撥打電話,必須先撥號給對方,等兩端確定連接后,相互才能聽到對方說話,也知道對方回應的是什么。
HTTP、FTP 和Telnet 等都需要使用可靠的通信頻道。例如,HTTP從某個URL讀取數據時,如果收到的數據順序與發送時不相同,可能就會出現一個混亂的HTML文件或是一些無效的信息。
UDP是無連接通信協議,不保證數據的可靠傳輸,但能夠向若干個目標發送數據,或接收來自若干個源的數據。UDP以獨立發送數據包的方式進行。這種方式就像郵遞員送信給收信人,可以寄出很多信給同一個人,且每一封信都是相對獨立的,各封信送達的順序并不重要,收信人接收信件的順序也不能保證與寄出信件的順序相同。
UDP 協議適合于一些對數據準確性要求不高,但對傳輸速度和時效性要求非常高的網站,如網絡聊天室、在線影片等。這是由于TCP協議在認證上存在額外耗費,可能使傳輸速度減慢,而UDP協議即使有一小部分數據包遺失或傳送順序有所不同,也不會嚴重危害該項通信。
端口與套接字
一般而言,一臺計算機只有單一的連到網絡的物理連接(Physical Connection),所有的數據都通過此連接對內、對外送達特定的計算機,這就是端口。網絡程序設計中的端口(port)并非真實的物理存在,而是一個假想的連接裝置。端口被規定為一個在0~65535的整數。HTTP服務一般使用80端口,FTP 服務使用21端口。假如一臺計算機提供了HTTP、FTP等多種服務,那么客戶機會通過不同的端口來確定連接到服務器的哪項服務上
通常,0~1023的端口數用于一些知名的網絡服務和應用,用戶的普通網絡應用程序應該使用1024以上的端口數,以避免端口號與另一個應用或系統服務所用端口沖突。
網絡程序中的套接字(Socket)用于將應用程序與端口連接起來。套接字是一個假想的連接裝置,就像插座一樣可連接電器與電線
?Java 將套接字抽象化為類,程序設計者只需創建Socket類對象,即可使用套接字。
TCP程序
TCP網絡程序設計是指利用Socket 類編寫通信程序。利用TCP協議進行通信的兩個應用程序是有主次之分的,一個稱為服務器程序,另一個稱為客戶機程序,兩者的功能和編寫方法大不一樣。
- 服務器程序創建一個 ServerSocket(服務器端套接字)對象,調用accept0方法等待客戶機來連接
- 客戶端程序創建一個Socket對象,請求與服務器建立連接
- 服務器接收客戶機的連接請求,同時創建一個新的Socket 對象與客戶建立連接。隨后服務器繼續等待新的請求
?InetAddress 類
java.net包中的InetAddress類是與IP地址相關的類,利用該類可以獲取IP地址、主機地址等信息。
例題1
package 例題;import java.net.InetAddress;
import java.net.UnknownHostException;public class 例題1 {public static void main(String[] args) {InetAddress ip; try {ip = InetAddress.getLocalHost();String lo = ip.getHostName();String lp = ip.getHostAddress();System.out.println("本機名" + lo);System.out.println("本機IP地址" + lp);} catch (UnknownHostException e) {e.printStackTrace();}}}
結果
ServerSocket 類
- ServerSocker 類的構造方法通常會拋出1OException異常,具體有以下幾種形式:
- ServerSocket():創建非綁定服務器套接字。
- ServerSocket(int port):創建綁定到特定端口的服務器套接字。
- ServerSocket(int port, int backlog):利用指定的backlog創建服務器套接字,并將其綁定到指定的本地端口號上。
- ServerSocket(int port, int backlog, InetAddress bindAddress):使用指定的端口、偵聽backlog和要綁定到的本地IP地址創建服務器。這種情況適用于計算機上有多塊網卡和多個I地址的情況,用戶可以明確規定ServerSocket在哪塊網卡或哪個IP地址上等待客戶的連接請求
ServerSocket 類的常用方法
?TCP網絡程序設計
例題2
客戶端
package 例題;import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;import javax.swing.*;
import javax.swing.event.*;public class 客戶端 extends JFrame{private PrintWriter wr;Socket so;private JTextArea area = new JTextArea();private JTextField text = new JTextField();public 客戶端() {setTitle("向服務器送數據");setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);Container con = getContentPane();JScrollPane scrollPane = new JScrollPane(area);getContentPane().add(scrollPane,BorderLayout.CENTER);con.add(text,"South");text.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {wr.println(text.getText().trim());area.append(text.getText() + '\n');text.setText("");}});}private void connect() {area.append("嘗試連接");try {so = new Socket("172.16.4.226",8888);wr = new PrintWriter(so.getOutputStream(),true);area.append("完成連接");}catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {客戶端 k = new 客戶端();k.setSize(200,200);k.setVisible(true);k.connect();}}
結果
?
服務端
package 例題;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;public class 例題2服務端 {private ServerSocket server;private Socket so;void start() {try {server = new ServerSocket(8888);System.out.println("服務器套接字已經創建成功");while(true) {System.out.println("等待客戶機鏈接");so = server.accept();BufferedReader re = new BufferedReader(new InputStreamReader(so.getInputStream()));while(true) {String me = re.readLine();if("exit".equals(me)) {System.out.println("客戶機退出");break;}System.out.println("客戶機" + me);}re.close();so.close();}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {例題2服務端 l = new 例題2服務端();l.start();}}
結果?
UDP程序
?
基于 UDP通信的基本模式如下:
- 將數據打包(稱為數據包),然后將數據包發往目的地。
- 接收別人發來的數據包,然后查看數據包。
發送數據包的步驟如下:
- 使用DatagramSocketO創建一個數據包套接字。
- 使用DatagramPacket(byte[] buf,int offset, int length,InetAddress address,int port)創建要發送的數據包。
- 使用DatagramSocket 類的sendO方法發送數據包。
接收數據包的步驟如下:
- 使用DatagramSocket(int port)創建數據包套接字,綁定到指定的端口。
- ? 使用 DatagramPacket(byte buf, int length)創建字節數組來接收數據包。
- 使用DatagramPacket 類的receive0方法接收UDP包。
DatagramPacket類
java.net 包的DatagramPacket 類用來表示數據包。DatagramPacket 類的構造方法如下:
DatagramPacket(byte[] buf, int length)
DatagramPacket(bytel] buf, int length, InetAddress address, int port)
第一種構造方法在創建DatagramPacket 對象時,指定了數據包的內存空間和大小。
第二種構造方法不僅指定了數據包的內存空間和大小,還指定了數據包的目標地址和端口
?
?
DatagramSocker類?
- java.net 包中的()
- DatagramSocket(int port)
- DatagramSocket(int port, InetAddress addr)
第一種構造方法創建DatagramSocket對象,構造數據報套接字,并將其綁定到本地主機任何可用的端口上。
第二種構造方法創建DatagramSocket對象,創建數據報套接字,并將其綁定到本地主機的指定端口上。
第三種構造方法創建DatagramSocket對象,創建數據報套接字,并將其綁定到指定的端口和指定的本地地址上。第三種構造函數適用于有多塊網卡和多個I地址的情況。
UDP網絡程序設計
例題3
客戶端
import java.io.IOException;
import java.net.*;public class Notification extends Thread {String weather = "節目預報:八點有大型晚會,請收聽";// 發送的消息int port = 9898; // 端口InetAddress iaddress = null;MulticastSocket socket = null; // 多點廣播套接字Notification() {try {iaddress = InetAddress.getByName("224.255.10.0"); // 實例化InetAddress,指定地址socket = new MulticastSocket(port); // 實例化多點廣播套接字socket.setTimeToLive(1); // 指定發送范圍是本地網絡socket.joinGroup(iaddress); // 加入廣播組} catch (IOException e) {e.printStackTrace(); // 輸出異常信息}}public void run() {while (true) {DatagramPacket packet = null; // 數據包byte data[] = weather.getBytes(); // 字符串消息的字節數組packet = new DatagramPacket(data, data.length, iaddress, port); // 將數據打包System.out.println(weather); // 控制臺打印消息try {socket.send(packet); // 發送數據sleep(3000); // 線程休眠} catch (IOException e) {e.printStackTrace(); } catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) { Notification w = new Notification();w.start(); // 啟動線程}
}
//21.3
結果
服務端
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import javax.swing.*;public class Receive extends JFrame implements Runnable, ActionListener {int port; // 端口InetAddress group = null; // 廣播組地址MulticastSocket socket = null; // 多點廣播套接字對象JButton inceBtn = new JButton("開始接收");JButton stopBtn = new JButton("停止接收");JTextArea inceAr = new JTextArea(10, 10); // 顯示接收廣播的文本域JTextArea inced = new JTextArea(10, 10);Thread thread;boolean stop = false; // 停止接受信息狀態public Receive() {setTitle("廣播數據報");setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);thread = new Thread(this);inceBtn.addActionListener(this); // 綁定按鈕ince的單擊事件stopBtn.addActionListener(this); // 綁定按鈕stop的單擊事件inceAr.setForeground(Color.blue); // 指定文本域中文字的顏色JPanel north = new JPanel(); // 創建Jpanel對象north.add(inceBtn); // 將按鈕添加到面板north上north.add(stopBtn);add(north, BorderLayout.NORTH); // 將north放置在窗體的上部JPanel center = new JPanel(); // 創建面板對象centercenter.setLayout(new GridLayout(1, 2)); // 設置面板布局center.add(inceAr); // 將文本域添加到面板上center.add(inced);add(center, BorderLayout.CENTER); // 設置面板布局validate(); // 刷新port = 9898; // 設置端口號try {group = InetAddress.getByName("224.255.10.0"); // 指定接收地址socket = new MulticastSocket(port); // 綁定多點廣播套接字socket.joinGroup(group); // 加入廣播組} catch (IOException e) {e.printStackTrace(); // 輸出異常信息}setBounds(100, 50, 360, 380); // 設置布局setVisible(true); // 將窗體設置為顯示狀態}public void run() { // run()方法while (!stop) {byte data[] = new byte[1024]; // 創建緩存字節數組DatagramPacket packet = null;packet = new DatagramPacket(data, data.length, group, port); // 待接收的數據包try {socket.receive(packet); // 接收數據包String message = new String(packet.getData(), 0, packet.getLength()); // 獲取數據包中的內容inceAr.setText("正在接收的內容:\n" + message); // 將接收內容顯示在文本域中inced.append(message + "\n"); // 每條信息為一行} catch (IOException e) {e.printStackTrace(); // 輸出異常信息}}}public void actionPerformed(ActionEvent e) { // 單擊事件if (e.getSource() == inceBtn) { // 單擊按鈕ince觸發的事件inceBtn.setBackground(Color.red); // 設置按鈕顏色stopBtn.setBackground(Color.yellow);if (!(thread.isAlive())) { // 如線程不處于“新建狀態”thread = new Thread(this); // 實例化Thread對象}thread.start(); // 啟動線程stop = false; // 開始接受信息}if (e.getSource() == stopBtn) { // 單擊按鈕stop觸發的事件inceBtn.setBackground(Color.yellow); // 設置按鈕顏色stopBtn.setBackground(Color.red);stop = true; // 停止接受信息}}public static void main(String[] args) {Receive rec = new Receive();rec.setSize(460, 200);}
}
結果