TCP/UDP 協議
通俗解釋:
TCP協議和UDP協議的區別類似於電話系統和郵政系統。
<1>TCP:類似於電話系統,建立雙向的通信通道,確定連接,話音順序接聽。
<2>UDP:類似於郵政系統,發送方將信件發送到正確的地址,但並不知道准確的郵路,大多數郵件到達了目的地,個別情況一些郵件會在路上丟失。郵件不保證順序到達目的地。
TCP套接字
Socket
客戶端的通信套接字,可指定遠端IP地址、端口進行連接通信,也可以通過方法獲取已連接的socket的遠端IP地址、端口,以及將此socket以字節輸入流和輸出流的形式返回,當與數據輸入流和輸出流綁定,便可實現客戶端的網絡通信。
構造函數均為public修飾類型,如果創建socket時發生I/O錯誤,均拋出IOException異常。常用構造函數如下:
Socket(InetAddress address,int port):創建一個socket並與規定的IP地址的指定的端口相連接。
Socket(String host,int port):創建一個socket並與指定主機的指定端口連接。
ServerSocket
服務器的通訊套接字,用來偵聽客戶端請求的連接,並為每個新連接創建一個socket對象,由此創建綁定此socket的輸入流和輸出流,與客戶端實現網絡通信。
構造函數均為public修飾類型,如果創建socket時發生I/O錯誤,均拋出IOException異常。常用構造函數如下:
ServerSocket(int port):在所給定的用來偵聽的端口上建立一個服務器套接字。如果端口號為0,則在任意的空閑的端口上建立要給服務器套接字。外來連接請求的數量默認最大為50。
多線程聊天室實例
服務端Cilent
創建ServerSocket並監聽設置的端口,調用accpet()方法,直到找到對應的socket與之連接,創建一個線程為止服務。
Launch the Application 部分為Java Swing的窗口部分。
public class ServerCilent { private JFrame frmServer; private JTextField textField; private JTable table; private JLabel lblNewLabel; private JButton btnRefresh; private JButton btnListenstart; private SocketManager SocketArr=new SocketManager(); //創建 Server端 void getServer(int port) { try { ServerSocket serverSocket=new ServerSocket(port); System.out.println("已經開啟服務端"); while(true) { Socket socket=serverSocket.accept(); new W_Thread(socket).start(); SocketArr.add(socket); SocketArr.sendClientInfo(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } class W_Thread extends Thread { Socket socket=null; private BufferedReader reader; private PrintWriter writer; public W_Thread(Socket socket) { this.socket=socket; } public void run() { try { reader=new BufferedReader(new InputStreamReader(socket.getInputStream())); writer=new PrintWriter(socket.getOutputStream(),true); String msg; while((msg=reader.readLine())!=null ) { System.out.println(msg); SocketArr.SendtoAll(msg); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { ServerCilent window = new ServerCilent(); window.frmServer.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the application. */ public ServerCilent() { initialize(); } /** * Initialize the contents of the frame. */ private void initialize() { frmServer = new JFrame(); frmServer.setResizable(false); frmServer.setTitle("ServerCilent"); frmServer.setBounds(100, 100, 452, 371); frmServer.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frmServer.getContentPane().setLayout(null); JPanel panel = new JPanel(); panel.setBorder(new TitledBorder(null, "DefaultSetting", TitledBorder.LEADING, TitledBorder.TOP, null, null)); panel.setBounds(10, 10, 414, 66); frmServer.getContentPane().add(panel); panel.setLayout(null); JLabel lblPort = new JLabel("Port:"); lblPort.setBounds(10, 25, 54, 15); panel.add(lblPort); textField = new JTextField(); textField.setBounds(48, 22, 122, 21); panel.add(textField); textField.setColumns(10); btnListenstart = new JButton("Listen&Start"); btnListenstart.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent arg0) { int port=Integer.parseInt(textField.getText()); ServerCilent serverCilent=new ServerCilent(); serverCilent.getServer(port); textField.setEditable(false); btnListenstart.setVisible(false); } }); btnListenstart.setBounds(254, 21, 136, 23); panel.add(btnListenstart); JLabel label = new JLabel("在線人數:"); label.setBounds(10, 298, 82, 15); frmServer.getContentPane().add(label); lblNewLabel = new JLabel(String.valueOf(SocketArr.size())); lblNewLabel.setBounds(75, 298, 54, 15); frmServer.getContentPane().add(lblNewLabel); btnRefresh = new JButton("Refresh"); btnRefresh.setBounds(331, 298, 93, 23); frmServer.getContentPane().add(btnRefresh); } }
多線程客戶端Cilent
創建Socket,並獲取對應的輸入輸出流即可。
public class Cilent implements Runnable{ /*Swing*/ private JFrame frmLogincilent; private JTextField IPAdress; private JTextField Port; private JTextField Nickname; private JTextField Sendinfo; private JButton Sumbit; private JTextArea ShowArea; private JButton Send; /*Stream*/ private BufferedReader reader; private PrintWriter writer; @Override public void run(){ while(true){ try { ShowArea.append(reader.readLine()+"\n"); } catch (Exception e) { // TODO: handle exception } } } public void Create_Socket(InetAddress ip,int port){ ShowArea.append("正在嘗試連接到服務端…… ……"+"\n"); try { Socket socket=new Socket(ip, port); ShowArea.append("聊天室已經准備好"); frmLogincilent.setTitle("Ip:"+ip+" Port:"+port+" 已連接在線"); reader=new BufferedReader(new InputStreamReader(socket.getInputStream())); writer=new PrintWriter(socket.getOutputStream(),true); new Thread(this).start(); } catch (Exception e) { // TODO: handle exception } } /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { Cilent window = new Cilent(); window.frmLogincilent.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the application. */ public Cilent() { initialize(); } /** * Initialize the contents of the frame. */ private void initialize() { frmLogincilent = new JFrame(); frmLogincilent.setResizable(false); frmLogincilent.setTitle("Cilent"); frmLogincilent.setBounds(100, 100, 458, 532); frmLogincilent.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frmLogincilent.getContentPane().setLayout(null); JPanel panel = new JPanel(); panel.setBorder(new TitledBorder(null, "Default Setting", TitledBorder.LEADING, TitledBorder.TOP, null, null)); panel.setBounds(10, 10, 424, 84); frmLogincilent.getContentPane().add(panel); panel.setLayout(null); JLabel lblIpAdress = new JLabel("IP Adress:"); lblIpAdress.setBounds(10, 20, 73, 15); panel.add(lblIpAdress); IPAdress = new JTextField(); IPAdress.setBounds(82, 17, 142, 21); panel.add(IPAdress); IPAdress.setColumns(10); JLabel lblProt = new JLabel("Prot:"); lblProt.setBounds(10, 48, 54, 15); panel.add(lblProt); Port = new JTextField(); Port.setBounds(82, 45, 66, 21); panel.add(Port); Port.setColumns(10); JLabel lblNickname = new JLabel("NickName:"); lblNickname.setBounds(170, 48, 73, 15); panel.add(lblNickname); Nickname = new JTextField(); Nickname.setBounds(238, 45, 176, 21); panel.add(Nickname); Nickname.setColumns(10); Sumbit = new JButton("Lock&Login"); Sumbit.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent arg0) { try { /*Create Socket*/ String ipString=IPAdress.getText(); int port=Integer.parseInt(Port.getText()); String Name=Nickname.getText(); InetAddress ip=InetAddress.getByName(ipString); Create_Socket(ip, port); /*Swing*/ IPAdress.setEditable(false); Port.setEditable(false); Nickname.setEditable(false); Sumbit.setVisible(false); } catch (Exception e) { // TODO: handle exception JOptionPane.showMessageDialog(null, "invalid data"); } } }); Sumbit.setBounds(272, 16, 142, 23); panel.add(Sumbit); ShowArea = new JTextArea(); ShowArea.setBounds(10, 104, 424, 358); frmLogincilent.getContentPane().add(ShowArea); Sendinfo = new JTextField(); Sendinfo.setBounds(10, 472, 330, 21); frmLogincilent.getContentPane().add(Sendinfo); Sendinfo.setColumns(10); Send = new JButton("Send"); Send.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent arg0) { writer.println(Nickname.getText()+":"+Sendinfo.getText()); Sendinfo.setText(""); } }); Send.setBounds(349, 471, 85, 23); frmLogincilent.getContentPane().add(Send); } }
Socket管理類
繼承ArrayList,並添加發送的方法SendtoAll(),以及顯示當前連接數目的方法SendCilentInfo()
public class SocketManager extends ArrayList{ synchronized void add(Socket x) { super.add(x); } synchronized void remove(Socket x) { super.remove(x); } synchronized void SendtoAll(String str) { PrintWriter writer=null; Socket socket; for(int i=0;i<size();i++) { socket=(Socket)get(i); try { writer=new PrintWriter(socket.getOutputStream(),true); if(writer!=null) { writer.println(str); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } synchronized void sendClientInfo() { String info="當前位於聊天室的人數為"+size(); System.out.println(info); SendtoAll(info); } }
示例效果: