JavaSE项目之聊天室swing版
引子:
当前,互联网 体系结构的参考模型主要有两种,一种是OSI参考模型,另一种是TCP/IP参考模型。
一、OSI参考模型,即开放式通信系统互联参考模型(OSI/RM,Open Systems Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。
OSI参考模型将实现网络互连的通信协议分为7层,自上而下分别是:
第7层应用层:OSI中的最高层,为用户提供各项互联网应用,如公司老板通过浏览器上网、发送电子邮件等。 常见的协议有:HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。
第6层表示层:相当公司中替老板写信的助理。
第5层会话层:相当于公司中收寄信、写信封与拆信封的秘书。
第4层传输层:提供终端到终端的可靠连接,相当于公司中跑邮局的送信职员。
第3层网络层: 确保信件通过一系列路由到达目的地。
第2层数据链路层: 决定访问网络介质的方式,并处理流控制。
第1层物理层:处于OSI参考模型的最底层,物理层的主要功能是利用物理传输介质为数据链路层提供物理连接,以便透明地传输比特流;该层的常用设备有网卡、集线器、中继器、调制解调器、网线、双绞线、同轴电缆等各种物理设备。
数据发送时,从第七层传到第一层,接收数据则相反。
上三层总称为“应用层”,用来控制软件方面;下四层总称为“数据流层”,用来管理硬件。除了物理层之外,其他层都是用软件实现的。
二、TCP/IP参考模型。
第四层应用层:协议有DNS、FTP、HTTP、HTTPS、Telnet、SMTP等;
第三层传输层:TCP、UDP等;
第二层网际层: IP、ICMP等;
第一层网络接口层:ARP、RARP等。
我们在对上述两种参考模型有些了解后,接下来主要看TCP和UDP。我们先来看二者的区别:
1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接;
2.TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付;
3.TCP面向字节流,实际上是TCP把数据看成是一连串无结构的字节流;UDP是面向报文的,它没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(如IP电话,实时视频会议等)
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信;
5.TCP首部开销20字节;UDP的首部开销小,只有8个字节;
6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。
在Java中,对遵守TCP协议的类有ServerSocket和Socket,遵守UDP协议的类有DatagramSocket。我这里提供的聊天室项目,是基于TCP协议的。
该项目分为4个包,分别是utils(提供工具),ui(提供窗体界面),server(服务器子线程),client(客户端子线程)。项目文件包结构如下图所示:
项目运行后的效果如下图所示:
接下来是项目代码:
package 聊天室swing版.utils; /** * 工具类,服务器ip,端口等信息 */ import java.net.InetAddress; import java.net.UnknownHostException; public abstract class HostInfo { public static String IP=getIP(); public static final int PORT=10086; public static final int NUM=50; public static final String NEW_LINE="\r\n"; private static String getIP(){ try { return InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); return null; } } } ----------------------------------------------------------- package 聊天室swing版.utils; /** * 工具类,释放资源 */ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public abstract class Release { public static void release(Socket socket,BufferedWriter bw){ release(null,socket,null,bw); } public static void release(Socket socket,BufferedReader br){ release(null,socket,br,null); } public static void release(Socket socket){ release(null,socket,null,null); } public static void release(ServerSocket server){ release(server,null,null,null); } public static void release(ServerSocket server,Socket socket,BufferedReader br,BufferedWriter bw){ if(server!=null){ try { server.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket!=null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(br!=null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if(bw!=null){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } } ----------------------------------------------------------------- package 聊天室swing版.server; /** * 服务器Server类 */ import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import 聊天室swing版.ui.UIserver; import 聊天室swing版.utils.HostInfo; import 聊天室swing版.utils.Release; public class Server extends Thread { private ServerSocket server; public static List<Transport> clients=new ArrayList<>(); @Override public void run() { try { server=new ServerSocket(Integer.parseInt(UIserver.jtf_port.getText().trim())); } catch (IOException e) { Release.release(server); throw new RuntimeException("服务器端口被占!"); } UIserver.bt_open.setText("已启动服务器"); UIserver.bt_open.setEnabled(false); UIserver.jta_log.append("服务器成功启动!"+HostInfo.NEW_LINE); new Accept().start(); } class Accept extends Thread{ private Socket socket; @Override public void run() { int num=0; while(num<HostInfo.NUM){ try { socket=server.accept(); } catch (IOException e) { Release.release(socket); throw new RuntimeException("客户端连接失败!"); } num++; String str="第 "+num+" 个客户端连接成功!==>"+socket.getInetAddress().getHostAddress()+" :"+socket.getPort()+HostInfo.NEW_LINE; UIserver.jta_log.append(str); clients.add(new Transport(socket)); } UIserver.jta_log.append("超出服务器负荷!"); Release.release(server); } } } ---------------------------------------------------- package 聊天室swing版.server; /** * 处理服务器Server类中接收客户端发来的信息,以及转发客户端发来的信息 */ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import 聊天室swing版.utils.Release; public class Transport extends Thread { private Socket socket; private String ip; public Transport(Socket socket){ this.socket=socket; this.ip=socket.getInetAddress().getHostAddress(); this.start(); } @Override public void run() { BufferedReader br=null; BufferedWriter ownbw=null; try { br=new BufferedReader(new InputStreamReader(socket.getInputStream())); ownbw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); } catch (IOException e) { Server.clients.remove(this); Release.release(socket); throw new RuntimeException("获取流失败!"); } String str=null; try { while((str=br.readLine())!=null){ String[] split = str.split(":", 2); if(split.length<=1){ ownbw.write("数据格式错误!"); ownbw.newLine(); ownbw.flush(); } String desip=split[0]; String content=split[1]; BufferedWriter desbw=null; boolean isOnLine=false; for(Transport des:Server.clients){ if(desip.equals(des.ip)){ isOnLine=true; desbw=new BufferedWriter(new OutputStreamWriter(des.socket.getOutputStream())); desbw.write(str); desbw.newLine(); desbw.flush(); } } if(!isOnLine){ ownbw.write("对方不在线!"); ownbw.newLine(); ownbw.flush(); } } } catch (IOException e) { Server.clients.remove(this); Release.release(socket); throw new RuntimeException("获取流失败!"); } } } ------------------------------------------------------------ package 聊天室swing版.client; /** * 客户端类,向服务器发送信息,以及接收服务器发来的信息 */ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import 聊天室swing版.ui.UIclient; import 聊天室swing版.ui.UIserver; import 聊天室swing版.utils.HostInfo; import 聊天室swing版.utils.Release; public class Client extends Thread { private Socket socket; @Override public void run() { try { socket=new Socket(UIserver.jtf_ip.getText().toLowerCase(), Integer.parseInt(UIserver.jtf_port.getText().trim())); } catch (IOException e) { Release.release(socket); throw new RuntimeException("客户端创建失败!"); } BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { Release.release(socket); throw new RuntimeException("获取流失败!"); } String str=null; try { while((str=br.readLine())!=null){ UIclient.jta_chat.append(str+HostInfo.NEW_LINE); } } catch (IOException e) { Release.release(socket); throw new RuntimeException("获取流失败!"); } } public void send(){ new Send().start(); } class Send extends Thread{ @Override public void run() { BufferedWriter bw=null; try { bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); } catch (IOException e) { Release.release(socket,bw); throw new RuntimeException("获取流失败!"); } String str=UIclient.jtf_desip.getText().trim()+":"+UIclient.jta_message.getText().trim(); try { bw.write(str); bw.newLine(); bw.flush(); } catch (IOException e) { Release.release(socket,bw); throw new RuntimeException("获取流失败!"); } } } } ------------------------------------------------------------- package 聊天室swing版.ui; /** * 服务器端UI * 要先开启服务器,再开启客户端 */ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; 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; import 聊天室swing版.server.Server; import 聊天室swing版.utils.HostInfo; public class UIserver extends JFrame { private JPanel jp=new JPanel(); private JLabel jl_ipTips=new JLabel("服务器ip:"); public static JTextField jtf_ip=new JTextField(HostInfo.IP); private JLabel jl_portTips=new JLabel("服务器端口:"); public static JTextField jtf_port=new JTextField(HostInfo.PORT+""); public static JButton bt_open=new JButton("启动服务器"); public static JTextArea jta_log=new JTextArea(); private JScrollPane jsp_log=new JScrollPane(jta_log); public UIserver(){ jp.setLayout(new FlowLayout()); jp.add(jl_ipTips); jp.add(jtf_ip); jp.add(jl_portTips); jp.add(jtf_port); jp.add(bt_open); jp.add(jsp_log); jl_ipTips.setPreferredSize(new Dimension(100, 50)); jtf_ip.setPreferredSize(new Dimension(150, 50)); jl_portTips.setPreferredSize(new Dimension(100, 50)); jtf_port.setPreferredSize(new Dimension(150, 50)); bt_open.setPreferredSize(new Dimension(260, 50)); jsp_log.setPreferredSize(new Dimension(260, 192)); jta_log.setLineWrap(true); jta_log.setEditable(false); bt_open.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { new Server().start(); } }); add(jp); setTitle("聊天应用控制服务器"); setBounds(100, 50, 300, 400); setResizable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new UIserver(); } } --------------------------------------------------------------- package 聊天室swing版.ui; /** * 客户端UI */ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import 聊天室swing版.client.Client; public class UIclient extends JFrame { private JPanel jp_chat=new JPanel(); public static JTextArea jta_chat=new JTextArea(); private JScrollPane jsp_chat=new JScrollPane(jta_chat); private JPanel jp_send=new JPanel(); public static JTextField jtf_desip=new JTextField("请输入对方Ip"); public static JTextArea jta_message=new JTextArea(); private JScrollPane jsp_message=new JScrollPane(jta_message); private JButton bt_send=new JButton("发送"); private Client client; public UIclient(){ jp_chat.add(jsp_chat); jta_chat.setLineWrap(true); jta_chat.setEditable(false); jsp_chat.setPreferredSize(new Dimension(550, 400)); jp_send.add(jtf_desip); jp_send.add(jsp_message); jta_message.setLineWrap(true); jp_send.add(bt_send); jtf_desip.setPreferredSize(new Dimension(100, 50)); jsp_message.setPreferredSize(new Dimension(250, 50)); bt_send.setPreferredSize(new Dimension(100, 50)); jtf_desip.addFocusListener(new FocusAdapter(){ @Override public void focusGained(FocusEvent e) { jtf_desip.setText(""); } }); jta_message.addFocusListener(new FocusAdapter(){ @Override public void focusGained(FocusEvent e) { jta_message.setText(""); } }); bt_send.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { client.send(); } }); add(jp_chat, BorderLayout.CENTER); add(jp_send, BorderLayout.SOUTH); setTitle("群聊窗口"); setBounds(300, 100, 600, 500); setResizable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); client=new Client(); client.start(); } public static void main(String[] args) { new UIclient(); } }
您可以在Eclipse上运行下,如果有问题,欢迎留言。希望对您有所帮助!