问题的原因是
while(connected) {
String str=dis.readUTF();
System.out.println(str);
}
不断循环执行,一直在死循环获取socket发送的信息,
使得前面的语句
s = ss.accept();
connected=true;
服务端接收新的客户端不能再被执行
解决的方法——通过多线程技术
当获得一个socket后,则开启一个多线程,让
while(connected) {
String str=dis.readUTF();
System.out.println(str);
}
循环读取操作在多线程中完成
这样每个socket获得之后都在各自的现成中完成自己的循环读取
具体方法:
首先在主函数main中建立一个关于线程的内部类,因为这个类外部其他类不需要,所以弄成内部类
解决问题后效果图:
服务器端代码有较大改动,当中遇到各种异常逐一解决,逻辑思路上不复杂,细节上注意解决了
先开启客户端的各种异常
客户端关闭后的异常提示
服务端增加了ServerSocket的关闭
服务端增加线程处理后无效果的调试——根据socket closed异常查明是
在内部类Client中while循环写在了try的外边,导致socket已经被关闭,但循环依然在尽心,不断提示出错
0.7版的最终服务器端代码如下:
package com.swift; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; public class ChatServer { public static void main(String[] args) { new ChatServer().fun(); } private void fun() { boolean started = false; ServerSocket ss = null; Socket s = null; try { ss = new ServerSocket(8888); started = true; } catch (BindException e) { System.out.println("端口使用中......"); } catch (IOException e1) { e1.printStackTrace(); } try { while (started) { s = ss.accept(); System.out.println("a client connected success"); Client c = new Client(s); new Thread(c).start(); } } catch (EOFException e) { System.out.println("client has closed."); } catch (Exception e) { e.printStackTrace(); } finally { try { ss.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class Client implements Runnable { private Socket s; private DataInputStream dis; private boolean connected = false; public Client(Socket s) { this.s = s; try { this.dis = new DataInputStream(s.getInputStream()); connected = true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { try {//注意:要包括while循环,如果try在while循环里,则出现socket closed异常 while (connected) { String str = dis.readUTF(); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (dis != null) { try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } if (s != null) { try { s.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }
0.7版本的客户端代码改动不大,代码如下:
package com.swift; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataOutputStream; import java.io.IOException; import java.net.ConnectException; 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 ChatClientFrame extends JFrame { private static final long serialVersionUID = -118470059355655240L; Socket s = null; DataOutputStream dos = null; JLabel label_shang = new JLabel(); JLabel label_xia = new JLabel(); JTextField tf = new JTextField(38); JTextArea ta = new JTextArea(15, 50); JButton button = new JButton(); public ChatClientFrame() { setBounds(200, 200, 500, 400); setTitle("客户端聊天工具 —— 0.7"); // 对窗口进行大的布局,分为三行一列,在pBasic面板上添加三个面板shang zhong xia JPanel pBasic = new JPanel(); pBasic.setLayout(new BorderLayout());// 不设置默认也是这种布局模式 setContentPane(pBasic);// 把面板放在窗口上,不记得用this.关键字 JPanel shang = new JPanel(); JPanel zhong = new JPanel(); JPanel xia = new JPanel(); // 设置JPanel面板的大小 shang.setSize(470, 25); zhong.setSize(470, 180); xia.setSize(470, 40); pBasic.add(shang, BorderLayout.NORTH); pBasic.add(zhong, BorderLayout.CENTER); pBasic.add(xia, BorderLayout.SOUTH); shang.setBackground(Color.red); zhong.setBackground(Color.yellow); xia.setBackground(Color.blue); label_shang.setText("聊天记录"); shang.add(label_shang); ta.setLineWrap(true);// 自动换行 JScrollPane scroll = new JScrollPane(ta);// 增加滚动条,以便不增加行数 zhong.add(scroll); label_xia.setText("输入信息"); xia.add(label_xia, BorderLayout.WEST); /* * 增加功能,窗口监听事件,窗口打开时设置光标焦点在tf文本域中 */ this.addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { tf.requestFocus(); } }); xia.add(tf, BorderLayout.CENTER); button.setText("发送"); xia.add(button, BorderLayout.EAST); final class ShareListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { String taText = ta.getText(); String tfText = tf.getText() + " "; String tfText1 = tf.getText(); ta.setText(taText + tfText); tf.setText(""); // 当回车或发送按钮时,tfText发送到服务器 try { dos.writeUTF(tfText1); dos.flush(); } catch (IOException e1) { e1.printStackTrace(); } } } button.addActionListener(new ShareListener()); tf.addActionListener(new ShareListener()); // 通过压缩自动调整各个面板 pack(); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { disconnect(); System.exit(0); } }); setVisible(true); // 创建窗体直接调用连接服务器 connect(); } public void connect() { try { s = new Socket("127.0.0.1", 8888); System.out.println("connected!"); dos = new DataOutputStream(s.getOutputStream()); } catch (ConnectException e) { System.out.println("服务端异常........."); System.out.println("请确认服务端是否开启........."); } catch (IOException e) { e.printStackTrace(); } } public void disconnect() { try { if (dos != null) dos.close(); if (s != null) s.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new ChatClientFrame(); } }