網絡編程-第二節
阿新 • • 發佈:2018-09-29
ali ica esc \n pla creates jbutton 主機 網絡
1.1 基礎題:客戶端與服務器持續通信
1.2 應用題:設計帶窗口客戶端
導入程序MyServer.java、MyClient.java、ClientThread.java。補充程序MyClient.java的代碼(在“//TODO 添加代碼”提示的位置),使得
1) 客戶端在運行之後打開如下窗口,用戶可以從文本框”Message:”中輸入信息,點擊“Send”之後,將信息發送給服務器,並將其顯示在文本區域中;
2) 客戶端接收到服務器回復的信息,將其顯示在文本區域”Information Border”中;
MyServer.java代碼如下:
packagecn.edu.sise; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import javax.swing.JOptionPane; /** * Echo服務器:根據Echo協議,接收來自客戶機消息,並立即回送。 * @author 董相誌,版權所有,2016--2018,[email protected]*/ public class MyServer extends javax.swing.JFrame { private ServerSocket listenSocket=null; //偵聽套接字 private Socket toClientSocket=null; //與客戶機對話的套接字 public static int clientCounts=0; //客戶數量編號 /** * Creates new form EchoServer */ public MyServer() { initComponents(); }/** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { topPanel = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); txtHostName = new javax.swing.JTextField(); jLabel2 = new javax.swing.JLabel(); txtHostPort = new javax.swing.JTextField(); btnStart = new javax.swing.JButton(); midPanel = new javax.swing.JPanel(); jScrollPane1 = new javax.swing.JScrollPane(); txtArea = new javax.swing.JTextArea(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle("My Server: 1707"); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { formWindowClosing(evt); } }); topPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Server Info Border", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("宋體", 1, 18))); // NOI18N jLabel1.setFont(new java.awt.Font("宋體", 1, 16)); // NOI18N jLabel1.setText("Host Name:"); txtHostName.setFont(new java.awt.Font("宋體", 1, 16)); // NOI18N txtHostName.setText("localhost"); jLabel2.setFont(new java.awt.Font("宋體", 1, 16)); // NOI18N jLabel2.setText("Port:"); txtHostPort.setFont(new java.awt.Font("宋體", 1, 16)); // NOI18N txtHostPort.setText("7"); btnStart.setFont(new java.awt.Font("宋體", 1, 16)); // NOI18N btnStart.setText("Start"); btnStart.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnStartActionPerformed(evt); } }); javax.swing.GroupLayout topPanelLayout = new javax.swing.GroupLayout(topPanel); topPanel.setLayout(topPanelLayout); topPanelLayout.setHorizontalGroup( topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(topPanelLayout.createSequentialGroup() .addContainerGap() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(txtHostName, javax.swing.GroupLayout.DEFAULT_SIZE, 131, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(txtHostPort, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(btnStart) .addContainerGap()) ); topPanelLayout.setVerticalGroup( topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(topPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel1) .addComponent(txtHostName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel2) .addComponent(txtHostPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnStart)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); getContentPane().add(topPanel, java.awt.BorderLayout.PAGE_START); midPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Information Border", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("宋體", 1, 18))); // NOI18N midPanel.setLayout(new java.awt.BorderLayout()); txtArea.setEditable(false); txtArea.setBackground(new java.awt.Color(204, 255, 255)); txtArea.setColumns(20); txtArea.setFont(new java.awt.Font("宋體", 1, 16)); // NOI18N txtArea.setRows(5); jScrollPane1.setViewportView(txtArea); midPanel.add(jScrollPane1, java.awt.BorderLayout.CENTER); getContentPane().add(midPanel, java.awt.BorderLayout.CENTER); pack(); }// </editor-fold>//GEN-END:initComponents //啟動服務器 private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStartActionPerformed try { btnStart.setEnabled(false); //禁用按鈕,避免重復啟動 String hostName=txtHostName.getText();//主機名 int hostPort=Integer.parseInt(txtHostPort.getText());//端口 //構建服務器的SocketAddress格式地址 SocketAddress serverAddr=new InetSocketAddress(InetAddress.getByName(hostName),hostPort); listenSocket=new ServerSocket(); //創建偵聽套接字 listenSocket.bind(serverAddr); //綁定到工作地址 txtArea.append("服務器開始等待客戶機連接...\n"); } catch (IOException ex) { } //創建一個匿名線程,用於偵聽和接受客戶機連接,並創建響應客戶機的會話線程 new Thread(new Runnable() { @Override public void run() { try { while (true) { //處理客戶機連接 toClientSocket=listenSocket.accept();//偵聽並接受客戶機連接 clientCounts++;//客戶機數量加1 txtArea.append(toClientSocket.getRemoteSocketAddress()+ " 客戶機編號: "+clientCounts+ " 會話開始...\n"); //創建客戶線程clientThread,實現一客戶一線程 Thread clientThread=new ClientThread(toClientSocket,clientCounts); clientThread.start(); //啟動任務線程 } //end while } catch (IOException ex) { JOptionPane.showMessageDialog(null, ex.getMessage(), "錯誤提示", JOptionPane.ERROR_MESSAGE); } }//end run() }).start(); }//GEN-LAST:event_btnStartActionPerformed //關閉服務器之前 private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing //關閉服務器之前釋放套接字 if (listenSocket!=null) listenSocket=null; if (toClientSocket!=null) toClientSocket=null; }//GEN-LAST:event_formWindowClosing /** * @param args the command line arguments */ public static void main(String args[]) { /* Set the Nimbus look and feel */ //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html */ try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException ex) { java.util.logging.Logger.getLogger(MyServer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (InstantiationException ex) { java.util.logging.Logger.getLogger(MyServer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { java.util.logging.Logger.getLogger(MyServer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(MyServer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } //</editor-fold> //</editor-fold> //</editor-fold> //</editor-fold> /* Create and display the form */ java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new MyServer().setVisible(true); } }); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnStart; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JPanel midPanel; private javax.swing.JPanel topPanel; public static javax.swing.JTextArea txtArea; private javax.swing.JTextField txtHostName; private javax.swing.JTextField txtHostPort; // End of variables declaration//GEN-END:variables }
MyClient.java代碼如下:
package cn.edu.sise; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class MyClient { public static void main(String[] args) { // TODO Auto-generated method stub // TODO 1.添加代碼:在窗口標題裏寫上自己的學號 JFrame frame = new JFrame("Client: 1707"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new MyPanel()); frame.setPreferredSize(new Dimension(600, 300)); frame.pack(); frame.setVisible(true); } } class MyPanel extends JPanel { JTextField tf; JTextArea ta; BufferedReader in = null; BufferedWriter out = null; public MyPanel() { this.setLayout(new BorderLayout()); JPanel p1 = new JPanel(); JLabel lb1 = new JLabel(); lb1.setFont(new java.awt.Font("宋體", 1, 16)); // NOI18N lb1.setText("Message:"); p1.add(lb1); tf = new JTextField(25); p1.add(tf); JButton btn = new JButton("Send"); btn.addActionListener(new MyListerner()); p1.add(btn); p1.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Communication Border", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("宋體", 1, 14))); // NOI18N this.add(p1, BorderLayout.NORTH); JPanel p2 = new JPanel(); p2.setLayout(new java.awt.BorderLayout()); p2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Information Border", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("宋體", 1, 14))); // NOI18N JScrollPane sp1 = new JScrollPane(); sp1.setPreferredSize(new java.awt.Dimension(8, 250)); ta = new JTextArea(5, 10); ta.setEditable(false); sp1.setViewportView(ta); p2.add(sp1, BorderLayout.CENTER); this.add(p2, BorderLayout.CENTER); try { Socket s = new Socket(); // TODO 2.添加代碼 1) 新建SocketAddress的對象,指定要連接的服務器; // 2)調用Socket的方法連接服務器 SocketAddress address=new InetSocketAddress("localhost",7); // 連接服務器 s.connect(address); in = new BufferedReader(new InputStreamReader(s.getInputStream())); // TODO 3.添加代碼 : 上面已給出輸入流的代碼,請補充輸出流的 out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); } catch (IOException ex) { System.out.println("異常信息:" + ex.getMessage()); } } class MyListerner implements ActionListener { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub String sendStr = tf.getText(); // 從文本框中讀取用戶輸入 tf.setText("");// 將文本框中的字符清空 // TODO 4.添加代碼:向服務器發送字符串 try{ out.write(sendStr); out.newLine(); out.flush(); }catch (IOException ex) { System.out.println("異常信息:"+ex.getMessage()); } System.out.println("向服務器發送字符串成功!" + sendStr); ta.append("To server: " + sendStr + "\n");// 將字符串顯示在文本區域 // TODO 5.添加代碼: 接收服務器回復的字符串,並顯示在文本區域 try{ ta.append("Recv: " + in.readLine() + ‘\n‘); }catch (IOException ex) { System.out.println("異常信息:"+ex.getMessage()); } } } }
ClientThread.java代碼如下:
package cn.edu.sise; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * * @author World */ public class ClientThread extends Thread { private Socket toClientSocket=null;//會話套接字 private BufferedReader in; //網絡輸入流 private PrintWriter out; //網絡輸出流 private int clientCounts=0;//在線客戶機總數 public ClientThread(Socket toClientSocket,int clientCounts) { //構造函數 this.toClientSocket=toClientSocket; this.clientCounts=clientCounts; } @Override public void run(){ try { // 創建綁定到套接字toClientSocket上的網絡輸入流與輸出流 in=new BufferedReader(new InputStreamReader(toClientSocket.getInputStream(),"UTF-8")); out=new PrintWriter(new OutputStreamWriter(toClientSocket.getOutputStream(),"UTF-8"),true); //5. 根據服務器協議,在網絡流上進行讀寫操作 String recvStr; while ((recvStr=in.readLine())!=null){ //只要客戶機不關閉,則反復等待和接收客戶機消息 Date date=new Date(); DateFormat format=new SimpleDateFormat("yyyy-mm-dd hh:mm:ss"); String time=format.format(date); MyServer.txtArea.append(toClientSocket.getRemoteSocketAddress()+ " 客戶機編號: "+clientCounts+" 消息:"+recvStr+" :"+time+"\n"); //解析並顯示收到的消息 //按照echo協議原封不動回送消息 out.println(toClientSocket.getLocalSocketAddress()+ " 客戶機編號: "+clientCounts+" Echo消息:"+recvStr+" : "+time); }//end while MyServer.clientCounts--; //客戶機總數減1 //遠程客戶機斷開連接,線程釋放資源 if (in!=null) in.close(); if (out!=null) out.close(); if (toClientSocket!=null) toClientSocket.close(); }catch (IOException ex) {} } //end run } //end class
運行結果:
網絡編程-第二節