仿照了网上的代码和思路对之前的1.0版本进行了修改,按别人的界面和思路来写代码巨难受
设计到的一些知识点
1.swing界面:
BorderLayout与FlowLayout的区别
BorderLayout类:
BorderLayout将版面分成EAST(东),SOUTH(南),WEST(西),NORTH(北),CENTER(中),五个区域块,并语序将组件放在指定的区域块内,因为将组件放入BorderLayout版面内会造成组件变形,所以不适合加入JButton(按钮)这类组件,而比较合适JPanel(面板);
FlowLayout类
FlowLayout的配置方式也是从左到右,从上到下,排列,如果窗口宽度足够,会将所有的语速放在同一行;如果宽度不够,自动换行。FlowLayout默认组件居中对齐,也可以按照需求左对齐或者右对齐 ,加入FlowLayout版面的组件也是按照顺序摆放的,所以也无法直接指定要摆放的位置。组价不会变形可以加入JButton这类组件。
另外还有个GridLayout(int rows, int cols, int hgap, int vgap) :
创建具有指定行数、列数以及组件水平、纵向一定间距的网格布局
JOptionPane类的使用:主要用于弹出提示框
showConfirmDialog():确认对话框
showInputDialog():输入对话框
showMessageDialog():消息对话框
showOptionDialog():选择对话框
2.针对窗口事件的监听类
addWindowListener(new WindowAdapter() { //窗口关闭后断开连接
@Override
public void windowClosing(WindowEvent e) {
try {
if (socket != null)
socket.close();
if (inputStream!= null)
inputStream.close();
if (outputStream != null)
outputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
})
addWindowListener:添加窗口事件监听器
WindowAdapter是抽象类,它是专门用于接收窗口事件的抽象适配器,由于抽象类不能被实例化,所以上面的程序中,创建了一个继承于WindowAdapter的匿名内部类,注意:继承并不需要重载所有方法,只要针对想处理的进行重写就可。
3.JTextArea中setText与append的区别
setText是让文本框显示你要的信息 而append则是在不改变文本框内容的基础上再它后面添加你的信息
textArea.setEditable(false);//设置为不可操作
如果要让文本框显示自己的信息则要用append
4.flush()方法的作用:flush()方法将输入流和输出流中的缓冲进行刷新,使缓冲区中的元素即时做输入和输出,而不必等缓冲区满
代码如下
客户端页面
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
//import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
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 TwoClientView extends JFrame implements ActionListener,KeyListener,Runnable{
private JTextArea textArea;
private JTextField textfield,tfname;
private JButton btnSend,btnId;
private JLabel label;
private JPanel jp1, jp2;
public boolean isConnect=false;
private Socket socket=null;
private DataInputStream inputStream=null;
private DataOutputStream outputStream=null;
private JScrollPane scrollpane;
private static TwoClientView view;
public JTextArea getTextArea() {
return textArea;
}
public DataInputStream getInputStream() {
return inputStream;
}
public DataOutputStream getOutputStream() {
return outputStream;
}
public static void main(String[] args) {
view=new TwoClientView();
TwoServiceView.clientviews.add(view);//添加到客户端集合中
Thread thread=new Thread(view);
thread.start();
}
public TwoClientView(){
initView();
try {
socket = new Socket("localhost", 9090);//连接本地服务器
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void initView() {
textArea=new JTextArea(20,20);
textArea.setEditable(false);//设置为不可操作
textfield=new JTextField(15);
scrollpane=new JScrollPane(textArea);//可以自动产生滚动轴
textfield.addKeyListener(this);
btnSend=new JButton("发送");
btnSend.addActionListener(this);
label=new JLabel("昵称");
tfname=new JTextField(8);
jp1=new JPanel();
jp2=new JPanel();
jp1.add(label);
jp1.add(tfname);
tfname.setText("用户0");
jp1.setLayout(new FlowLayout(FlowLayout.CENTER));
jp2.add(textfield);
jp2.add(btnSend);
jp2.setLayout(new FlowLayout(FlowLayout.CENTER));
this.add(jp1,BorderLayout.NORTH);
this.add(scrollpane, BorderLayout.CENTER);
this.add(jp2, BorderLayout.SOUTH);
this.setTitle("聊天室");
setSize(500, 500);
setLocation(450, 150);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
try {
if(socket!=null) socket.close();//如果socket本身就是null关不关也就无所谓了
if(inputStream!=null) inputStream.close();
if(outputStream!=null) outputStream.close();
}catch(IOException e1){
e1.printStackTrace();
}
}
});
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btnSend) {
SendMsg();
}
}
private void SendMsg() {
try {
String str=textfield.getText();
if(!str.equals("")) {
textfield.setText("");
textArea.append(tfname.getText()+":\n"+str+"\n");//将内容发到自身的area上
outputStream=new DataOutputStream(socket.getOutputStream());
outputStream.writeUTF(tfname.getText()+"#"+str);//将内容读入文件输出流
outputStream.flush();//清空缓存数据
}
}
catch(IOException e) {
e.printStackTrace();
}
}
@Override
public void keyPressed(KeyEvent arg0) {//也可使用回车键发送
if (arg0.getKeyCode() == KeyEvent.VK_ENTER) {
SendMsg();
}
}
@Override
public void keyReleased(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
@Override
public void run() {
try {
inputStream=new DataInputStream(socket.getInputStream());
while(true) {
String []s=inputStream.readUTF().split("#");
textArea.append(s[0]+":\n"+s[1]+"\n");//用于向其他用户发送信息
}
}
catch(IOException e) {
e.printStackTrace();
}
}
}
2服务端页面
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TwoServiceView extends JFrame implements ActionListener{
private JButton btnOpen,btnStop;
private JLabel label;
private TwoService service=null;
public static ArrayList<TwoClientView> clientviews=new ArrayList<>();
private static TwoServiceView view;
public static TwoServiceView getView() {
return view;
}
public static void main(String[] args) {
view=new TwoServiceView();
}
public TwoServiceView() {
initView();
}
private void initView() {
btnOpen=new JButton("打开服务器");
btnStop=new JButton("关闭服务器");
btnStop.setEnabled(false);
btnOpen.addActionListener(this);
btnStop.addActionListener(this);
label=new JLabel("服务器停止工作");
add(label);
add(btnOpen);
add(btnStop);
setTitle("服务器");
setLayout(new GridLayout(3, 1,0,10));
setSize(300, 300);
setLocation(450, 450);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btnOpen) {
open();
}else {
stop();
}
}
public void open() {
service=new TwoService();
Thread thread=new Thread(service);
thread.start();
label.setText("服务器正在运行");
btnOpen.setEnabled(false);
btnStop.setEnabled(true);
}
public void stop() {
label.setText("服务器已关闭");
btnOpen.setEnabled(true);
btnStop.setEnabled(false);
try {
synchronized (TwoClientMannager.sockets) {//使用sockets作为同步监视器,任何线程进入同步代码块必须先
for(TwoCharSocket socket:TwoClientMannager.sockets) {//获得对sockets的锁定
socket.getInputStream().close();
socket.getOutputStream().close();
}
TwoClientMannager.sockets.removeAllElements();//删除集合中的所有客户端信息
}
for(TwoClientView view: clientviews) {
view.getInputStream().close();
view.getOutputStream().close();
}
service.getserviceSocket().close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
3开启服务端连接
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JOptionPane;
public class TwoService implements Runnable{//本类用来开启服务端连接
private ServerSocket serversocket=null;
public ServerSocket getserviceSocket() {
return serversocket;
}
public void run() {
try {
serversocket=new ServerSocket(9090);//建立与客户端的连接
while(true) {
Socket socket=serversocket.accept();
JOptionPane.showMessageDialog(TwoServiceView.getView(), "客户端连接端口", "TIP", JOptionPane.INFORMATION_MESSAGE);
TwoCharSocket chatsocket=new TwoCharSocket(socket);
TwoClientMannager.sockets.add(chatsocket);//每建立起一个连接就将它添加到连接集合中去
Thread thread=new Thread(chatsocket);//启动线程不断接收客户端信息
thread.start();//开启服务端连接通道
}
}catch(IOException e) {
e.printStackTrace();
System.out.println("服务器关闭");
}
}
}
4。开启客户端连接
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class TwoCharSocket implements Runnable{开启服务端连接
private Socket socket=null;
private DataInputStream inputStream = null;
private DataOutputStream outputStream = null;
public DataInputStream getInputStream() {
return inputStream;
}
public DataOutputStream getOutputStream() {
return outputStream;
}
public TwoCharSocket(Socket socket) {
this.socket=socket;
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(String str) {//用于将所有的信息输入服务端的输出流
try {
outputStream.writeUTF(str);
outputStream.flush();
}catch(IOException e) {
e.printStackTrace();
}
}
public void run() {
String accept=null;
while(true) {
try {
accept=inputStream.readUTF();
TwoClientMannager.sendAll(this, accept);
}catch(IOException e) {
TwoClientMannager.sockets.remove(this);
}
}
}
}
5向所有客户端发送信息的操作
import java.util.Vector;
public class TwoClientMannager {//用于向所有客户端发送信息的操作
private TwoClientMannager() {
}
public static Vector<TwoCharSocket> sockets=new Vector<>();
public static void sendAll(TwoCharSocket charSocket,String send) {
for(TwoCharSocket s:sockets) {
if(!charSocket.equals(s)) {//判断是不是自身
s.send(send);
}
}
}
}