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); } }
示例效果: