計算機網絡中,客戶機與服務器之間進行通訊時,客戶機必須知道服務器的IP地址和端口號。IP地址是標識Internet網絡中的某臺計算機,而端口號則標識着在服務器上運行的某個程序(進程),若是在服務器上運行的程序,沒有端口號,則客戶端的程序就不能找到它,也不能和它進行通訊。必定要清楚,別和電腦上的物理端口號搞混了。 端口號是一個邏輯的地址,用於標識計算機中運行着的應用程序。它能夠是0~65535之間的一個數,0~1023爲系統保留使用,例如http的是80端口等,這些是系統已經分配給默認服務使用的。 當兩個應用程序須要經過網絡通訊時,就可使用IP地址和端口號進行經過,IP地址和端口號組合在一塊兒就是一個Socket(網絡套接字)。套接字分爲服務端的和客戶端的兩種java
客戶端的套接字用於與服務器端的套接字進行鏈接。其使用方法以下:編程
socket=new Socket("127.0.0.1",2018);緩存
其中127.0.0.1標識服務器端的IP地址,2018標識服務器端應用程序的端口號。在創建套接字的過程當中,可能會發生IOException異常,要注意進行處理。服務器
客戶端與服務器端通訊時,服務器端負責提供服務,客戶端經過Socket向服務器端請求服務。所以,在服務器端也必須有相應的套接字與客戶端進行鏈接。使用以下:網絡
try { serverSocket=new ServerSocket(2018); socket=serverSocket.accept(); } catch (IOException e) { e.printStackTrace(); }
ServerSocket的構造方法中,只有一個參數,即Port號,須要注意的是,服務器端的Port號必須與客戶端的相同。多線程
當調用ServerSocket的accept()方法時,會返回一個客戶端套接字對象,即Socket對象。該對象在客戶端與服務端鏈接的過程當中,將駐留在服務器的內存中,咱們經過它就能夠與客戶端進行數據的傳輸。app
客戶端與服務端通訊時,首先服務端要在相應的端口進行監聽,等待客戶端的鏈接請求。而後在客戶端請求時,接受客戶端的請求,而後進行通訊。首先是服務端。代碼以下:socket
package socket.demo2; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class MyServerFrame extends JFrame{ //接收客戶端發送的消息 private JTextArea allMessage=new JTextArea(8,20); // 輸入發送給客戶端的消息 private JTextField sendMessage=new JTextField(20); //消息發送按鈕 private JButton sendBtn=new JButton("發送"); private JButton startServer=new JButton("開始服務器"); private ServerSocket serverSocket=null;//服務器端Socket private BufferedReader in=null;//用於接收客戶端發送的數據 private BufferedWriter out=null;//用於發送數據給客戶端 private Socket socket=null;//客戶端鏈接對象 private int port; public void setPort(int port){ this.port=port; } public MyServerFrame(){ init(); } private void init(){ setTitle("服務端"); setLayout(new BorderLayout()); //開始服務器按鈕單擊事件 startServer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { serverSocket=new ServerSocket(port); socket=serverSocket.accept(); new receiverMsgThread().start();//啓動線程接收客戶端數據 } catch (IOException e1) { e1.printStackTrace(); } } }); //發送按鈕單擊事件 sendBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); out.write(sendMessage.getText()); out.newLine(); out.flush(); } catch (IOException e1) { e1.printStackTrace(); } } }); //將組件添加到窗體中 JPanel panel=new JPanel(); panel.add(startServer); panel.add(sendMessage); panel.add(sendBtn); add(allMessage,BorderLayout.CENTER); add(panel,BorderLayout.SOUTH); //配置窗體 setSize(500,300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } //接收信息線程 class receiverMsgThread extends Thread{ public void run(){ try { in=new BufferedReader(new InputStreamReader(socket.getInputStream())); String str=null; while (true){ str=in.readLine()+"\n"; allMessage.append("客戶端說:"+str); } } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { MyServerFrame myServerFrame=new MyServerFrame(); myServerFrame.setPort(12345); } }
客戶端代碼以下:ide
package socket.demo2; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.net.Socket; public class MyClientFrame extends JFrame{ //接收客戶端發送的消息 private JTextArea allMessage=new JTextArea(8,20); // 輸入發送給客戶端的消息 private JTextField sendMessage=new JTextField(20); //消息發送按鈕 private JButton sendBtn=new JButton("發送"); private JButton connServer=new JButton("鏈接服務器"); private Socket socket=null; private BufferedReader in=null; private BufferedWriter out=null; public MyClientFrame(){ init(); } private void init(){ setTitle("客戶端"); setLayout(new BorderLayout()); //鏈接服務器按鈕單擊事件 connServer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { socket=new Socket("127.0.0.1",12345); System.out.println("鏈接成功"); new receiverMsgThread().start(); } catch (IOException e1) { e1.printStackTrace(); } } }); //發送按鈕單擊事件 sendBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); out.write(sendMessage.getText()); out.newLine(); out.flush(); System.out.println("222"); } catch (IOException e1) { e1.printStackTrace(); } } }); //將組件添加到窗體中 JPanel panel=new JPanel(); panel.add(connServer); panel.add(sendMessage); panel.add(sendBtn); add(allMessage,BorderLayout.CENTER); add(panel,BorderLayout.SOUTH); //配置窗體 setSize(500,300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } class receiverMsgThread extends Thread{ public void run(){ InputStream is=null; BufferedReader br=null; String str=null; try { is=socket.getInputStream(); br=new BufferedReader(new InputStreamReader(is)); while(true){ str=br.readLine()+"\n"; allMessage.append("服務器說:"+str); } } catch (IOException e) { e.printStackTrace(); } System.out.println("客戶端"); } } public static void main(String[] args) { new MyClientFrame(); } }
程序運行界面以下:this
目前爲止,服務端只能與一個客戶端進行通訊。在編寫的過程當中,因爲對BufferedReader類,以前並不瞭解,所以在編寫的過程當中,遇到了很大的問題,致使走了不少的彎路。最後經過查閱其文檔,解決了問題。主要是由於其readLine()方法,在發送數據時,它是以回車符做爲結束的,不然它不會結束,從而致使後續的操做沒法完成,所以在發送數據時,必定要注意,代碼以下:
out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); out.write(sendMessage.getText());//這個只是將數據寫入到緩存中,並不真正進行發送 out.newLine();//這是一個新行,標識傳輸數據的結束,這點必定要注意 out.flush();//這個是將緩存中的數據進行發送
以上程序代碼中,服務器只能接收一個客戶端的鏈接,下面對服務器端進行改進,實現多個客戶端的鏈接。
思想以下:採用多線程方式,每個客戶端鏈接時,啓動一個線程負責該客戶端的通訊,將產生的客戶端Socket放入到一個集合中,而後,當客戶端發送數據時,服務器端將收到的數據轉發給全部的客戶端。服務端修改後的代碼以下;
package socket.demo2; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class MyServerFrame extends JFrame{ //保存客戶端的Socket,用於向全部的客戶端進行數據的轉發。 private static List<Socket> clients=new ArrayList<Socket>(); //接收客戶端發送的消息 private JTextArea allMessage=new JTextArea(8,20); // 輸入發送給客戶端的消息 private JTextField sendMessage=new JTextField(20); //消息發送按鈕 private JButton sendBtn=new JButton("發送"); private JButton startServer=new JButton("開始服務器"); private ServerSocket serverSocket=null;//服務器端Socket private BufferedReader in=null;//用於接收客戶端發送的數據 private BufferedWriter out=null;//用於發送數據給客戶端 private Socket socket=null;//客戶端鏈接對象 private int port; public void setPort(int port){ this.port=port; } public MyServerFrame(){ init(); } private void init(){ setTitle("服務端"); setLayout(new BorderLayout()); //開始服務器按鈕單擊事件 startServer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { startServer.setEnabled(false); //啓動鏈接線程 try { serverSocket=new ServerSocket(port); } catch (IOException e1) { e1.printStackTrace(); } new connClient().start(); } }); //發送按鈕單擊事件 sendBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { for(Socket s:clients){ out=new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); out.write(sendMessage.getText()); out.newLine(); out.flush(); } } catch (IOException e1) { e1.printStackTrace(); } } }); //將組件添加到窗體中 JPanel panel=new JPanel(); panel.add(startServer); panel.add(sendMessage); panel.add(sendBtn); add(allMessage,BorderLayout.CENTER); add(panel,BorderLayout.SOUTH); //配置窗體 setSize(500,300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } class connClient extends Thread{ public void run(){ while(true){ try { socket=serverSocket.accept(); clients.add(socket); new sendMsgToClients(socket).start(); new receiverMsgThread().start();//啓動線程接收客戶端數據 } catch (IOException e1) { e1.printStackTrace(); } } } } //接收信息線程 class receiverMsgThread extends Thread{ public void run(){ try { in=new BufferedReader(new InputStreamReader(socket.getInputStream())); String str=null; while (true){ str=in.readLine()+"\n"; allMessage.append("客戶端說:"+str); } } catch (IOException e) { e.printStackTrace(); } } } class sendMsgToClients extends Thread{ private Socket clientSocket; public sendMsgToClients(Socket clientSocket){ this.clientSocket=clientSocket; } public void run(){ //首先接收客戶端發送過來的信息, //而後向客戶端,進行信息的轉發。 try { while(true){ in=new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String str=in.readLine(); for(Socket s:clients){ out=new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); out.write(str); out.newLine(); out.flush(); } } } catch (IOException e) { e.printStackTrace(); clients.remove(socket); } } } public static void main(String[] args) { MyServerFrame myServerFrame=new MyServerFrame(); myServerFrame.setPort(12345); } }