不積跬步,無以至千里;不積小流,無以成江海。——《荀子勸學》
JAVA 中設計網絡編程模式的主要有TCP和UDP兩種。
TCP 是屬於即時通信,點對點連接進行通信。
UDP 是通過數據包來進行通信,UDP當中就會牽扯到數據的解析和傳送。
在安全性能方面,TCP 要略勝一籌,通信過程中不容易出現數據丟失的現象,有一方中斷,兩方的通信就會結束。
UDP 數據包傳送的過程當中,一方中斷,數據包有很大的可能丟失,還有可能傳來的數據包的順序是錯亂的。
在效率方面,UDP 要比 TCP 快的不只是一點點的問題,若終端有解析數據方法的函數,數據包就會源源不斷的傳送過來,然后反饋回去。
public class Server { static BufferedReader br; //服務器端的輸入流 static PrintStream ps; //服務器端的輸出流 static JTextArea text; //服務器相關的界面組件 public Server() { JFrame frame = new JFrame("服務器端"); text = new JTextArea(); JScrollPane scroll = new JScrollPane(text); frame.add(scroll); frame.setVisible(true); frame.setSize(300, 400); text.setEditable(false); } public static void main(String[] args) throws Exception { new Server(); //生成服務器界面 //通過服務器端構造函數 ServerSocket(port) 實例化一個服務器端口 ServerSocket server = new ServerSocket(2000); text.append("監聽2000端口" + "\n"); Socket client = server.accept(); br = new BufferedReader(new InputStreamReader(client.getInputStream())); ps = new PrintStream(client.getOutputStream()); String msg; //如果輸入流不為空,將接受到的信息打印到相應的文本框中並反饋回收到的信息 while ((msg = br.readLine()) != null) { text.append("服務器端收到:" + msg + "\n"); ps.println(msg); if (msg.equals("quit")) { text.append("客戶端“2000”已退出!" + "\n"); text.append("服務器程序將退出!"); break; } } ps.close(); br.close(); client.close(); } }
Client類:
public class Client implements ActionListener { //這里有兩個圖形界面,一個是連接的frame,另一個和服務器通信的界面frame1 private JFrame frame; private JLabel adress; private JLabel port; JTextField adresstext; JTextField porttext; JButton connect; private JFrame frame1; private JLabel shuru; private JPanel panel1; private JPanel panel2; private JLabel jieshou; JButton send; static JTextArea shurukuang; static TextArea jieshoukuang; static BufferedReader br1; //從服務端接受的數據流 static PrintStream ps; //從客戶端輸出的數據流 static BufferedReader br2; //從通信界面中的輸入框接受的數據流 static Socket client; //將輸入框字符串轉換為字符串流所需的字符串的輸入流 static ByteArrayInputStream stringInputStream; public Client() { frame = new JFrame(); adress = new JLabel("IP 地址"); port = new JLabel("端口號"); adresstext = new JTextField("127.0.0.1", 10); porttext = new JTextField("2000", 10); connect = new JButton("連接"); //連接界面的布局 frame.setLayout(new FlowLayout()); frame.add(adress); frame.add(adresstext); frame.add(port); frame.add(porttext); frame.add(connect); frame.setVisible(true); frame.setSize(200, 150); connect.addActionListener(this); //通信界面的實例化 frame1 = new JFrame(); shuru = new JLabel("請輸入"); shurukuang = new JTextArea("請輸入····", 5, 40); panel1 = new JPanel(); panel1.add(shuru); panel1.add(shurukuang); panel1.setLayout(new FlowLayout()); send = new JButton("發送"); panel2 = new JPanel(); jieshou = new JLabel("已接受"); jieshoukuang = new TextArea(8, 60); jieshoukuang.setEditable(false); panel2.add(jieshou); panel2.add(jieshoukuang); panel2.setLayout(new FlowLayout()); frame1.setLayout(new FlowLayout()); //通信界面都的布局 frame1.add(BorderLayout.NORTH, panel1); frame1.add(send); frame1.add(BorderLayout.SOUTH, panel2); //連接時通信界面是處於看不到的 frame1.setVisible(false); frame1.setSize(500, 350); send.addActionListener(this); } //兩個界面當中都有相應的按鈕時間,為相應的時間添加動作 public void actionPerformed(ActionEvent e) { if (e.getSource() == connect) { try { //當觸發連接按鈕時,實例化一個客戶端 client = new Socket("127.0.0.1", 2000); //隱藏連接界面,顯示通信界面 frame.setVisible(false); frame1.setVisible(true); jieshoukuang.append("已經連接上服務器!" + "\n"); } catch (IOException e1) { System.out.println("鏈接失敗!"); e1.printStackTrace(); } } //通信界面中的發送按鈕相應的時間處理 if (e.getSource() == send) { //將輸入框中的字符串轉換為字符串流 stringInputStream = new ByteArrayInputStream((shurukuang.getText()).getBytes()); br2 = new BufferedReader(new InputStreamReader(stringInputStream)); String msg; try { while ((msg = br2.readLine()) != null) { ps.println(msg); //將輸入框中的內容發送給服務器端 jieshoukuang.append("向服務器發送:" + msg + "\n"); jieshoukuang.append("客戶端接受相應:" + br1.readLine() + "\n"); if (msg.equals("quit")) { jieshoukuang.append("客戶端將退出!"); br1.close(); ps.close(); client.close(); frame1.setVisible(false); break; } } } catch (IOException e2) { System.out.println("讀輸入框數據出錯!"); } shurukuang.setText(""); } } public static void main(String[] args) throws IOException { new Client(); //實例化連接界面 client = new Socket("127.0.0.1", 2000); br1 = new BufferedReader(new InputStreamReader(client.getInputStream())); //從服務端接受的數據 ps = new PrintStream(client.getOutputStream()); //從客戶端輸出的數據 } }
寫完這兩個類以后還是有幾個問題:
1)main 函數為什么非要用 static 來修飾?
2)緩沖對象 BufferedReader 為什么不能直接用於判斷,非要將讀到的數據賦值給字符串來進行操作?
3)在連接界面當中的 Connect 按鈕事件 當中我有實例化一個 客戶端的對象,注釋掉主函數中 client=new Socket("127.0.0.1",2000)會發現拋出 NULLPOINTEXCEPTION 異常,我很不理解?