JavaSE聊天室

今天学习了一下简单聊天程序(类似QQ那种)的编写过程,从最初的0.1版本到最后的1.3版本,功能不断地增强,下面对一天的学习内容进行梳理。

版本0.1

我们的需求是显示一个窗体,其他什么也不用做,其他功能逐步添加,我们这里用的就是AWT中的Frame;

具体代码实现:

 import java.awt.*;

 public class ChatClient extends Frame{

     /**
* @param args
*/
public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.1
*/
public void launchFrame(){
//设置Frame位置
setLocation(400, 300);
//设置Frame大小
this.setSize(300, 300);
//窗口可见
setVisible(true);
}
}

版本0.2

我们的需求是在我们的Frame中添加两个部件TextField(用于输入)和TextArea(用于显示获取的内容),此时我们可以在输入框输入内容,但是显示框无法显示我们输入的内容.

 import java.awt.*;

 public class ChatClient extends Frame {

     TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.2
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 窗口可见
setVisible(true);
}
}

版本0.3

因为我们的版本0.2中,显示的窗体无法关闭(除非把程序停掉,我们这里不考虑这种做法),我们添加窗口监听,使我们可以通过点击窗体的(X)符号进行关闭;

 import java.awt.*;
import java.awt.event.*; public class ChatClient extends Frame { TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.3:添加窗口关闭的功能
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
//添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}); // 窗口可见
setVisible(true);
}
}

版本0.4

我们实现的功能是将输入框输入的内容显示到显示框TextArea中,

 import java.awt.*;
import java.awt.event.*; public class ChatClient extends Frame { TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.4
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
//获得输入框的内容,并去除两端的空格
String s = tfTxt.getText().trim();
//将获取的输入内容放置到TextArea中
taContent.setText(s);
//每次输入结束,将输入框置空
tfTxt.setText("");
}
}
}

版本0.5

相比版本0.4,我们添加了Server端,使得Server作为中转站,把我们输入的内容显示在其他连接到Server端的客户端的的显示框中;

ChatClient

 import java.awt.*;
import java.awt.event.*; public class ChatClient extends Frame { TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.5:处理输入框
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
// 获得输入框的内容,并去除两端的空格
String s = tfTxt.getText().trim();
// 将获取的输入内容放置到TextArea中
taContent.setText(s);
// 每次输入结束,将输入框置空
tfTxt.setText("");
}
}
}

Server

 import java.io.IOException;
import java.net.*; //server端
public class ChatServer { /**
* @param args
*/
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8888);
while(true){
Socket s=ss.accept();
System.out.println("a clint connected");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

此时,我们可以运行多个客户端,但是每个客户端输入的内容在其他客户端都无法显示,他们都是孤立的,还没有与Server端建立连接;

版本0.6

ChatClient

 import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*; public class ChatClient extends Frame {
Socket s = null; TextField tfTxt = new TextField(); TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.6
*/
public void launchFrame() {
setLocation(400, 300);
this.setSize(300, 300);
add(tfTxt, BorderLayout.SOUTH);
add(taContent, BorderLayout.NORTH);
pack();
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent arg0) {
System.exit(0);
} });
tfTxt.addActionListener(new TFListener());
setVisible(true);
connect();
} public void connect() {
try {
s = new Socket("127.0.0.1", 8888);
System.out.println("connected!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } private class TFListener implements ActionListener { public void actionPerformed(ActionEvent e) {
String str = tfTxt.getText().trim();
taContent.setText(str);
tfTxt.setText(""); try {
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF(str);
dos.flush();
dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} } } }

Server

 import java.io.*;
import java.net.*; public class ChatServer { public static void main(String[] args) {
try {
// 服务器端需要创建监听端口的 ServerSocket, ServerSocket 负责接收客户连接请求
ServerSocket ss = new ServerSocket(8888);
while (true) {
//服务器端应对客户端的每一个连接建立一个新的Socket(重要)
Socket s = ss.accept();
// 当有客户端连接上时,打印连接信息
System.out.println("a client connected!"); //获取Socket s的输入流
DataInputStream dis = new DataInputStream(s.getInputStream());
//获取输入流中的信息
String str = dis.readUTF();
System.out.println(str);
//关闭流操作
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} }

我们此时客户端输入的内容可以显示在Server端的控制台上,每个连接上的Client的输入只有第一次输入可以显示在控制台,每个客户端不可以接收其他客户端的信息。并且当我们某些关闭处于连接状态的客户端的时候Server端会报Socket异常;

版本0.7

ChatClient

 import java.awt.*;
import java.awt.event.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*; public class ChatClient extends Frame { // 暴露Socket
Socket s = null;
TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.7
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
connect();
} // 建立連接的方法
public void connect() {
try {
s = new Socket("127.0.0.1", 8888);
System.out.println("connected!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
// 获得输入框的内容,并去除两端的空格
String string = tfTxt.getText().trim();
// 将获取的输入内容放置到TextArea中
taContent.setText(string);
// 每次输入结束,将输入框置空
tfTxt.setText("");
try {
System.out.println(s);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF(string);
dos.flush();
dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} }
}
}

Server

 import java.io.DataInputStream;
import java.io.IOException;
import java.net.*; //server端
public class ChatServer { /**
* @param args
*/
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8888);
while(true){
Socket s=ss.accept();
System.out.println("a clint connected");
DataInputStream dis=new DataInputStream(s.getInputStream());
String str=dis.readUTF();
System.out.println(str);
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} }

相较于版本0.6,做了一些调试工作,每次打印System.out.println(s);观察出错信息,关闭客户端时,仍然会出现Socket is closed异常

版本0.8

ChatClient

 import java.awt.*;
import java.awt.event.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*;
public class ChatClient extends Frame { //暴露Socket
Socket s=null;
DataOutputStream dos=null;
TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.8
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
//窗口关闭的时候释放连接资源
disconnect();
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
connect();
}
//建立連接的方法
public void connect(){
try {
s=new Socket("127.0.0.1",8888);
dos=new DataOutputStream(s.getOutputStream());
System.out.println("connected!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void disconnect(){
try {
dos.close();
s.close();
} catch (Exception e) {
e.printStackTrace();
}
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
// 获得输入框的内容,并去除两端的空格
String string = tfTxt.getText().trim();
// 将获取的输入内容放置到TextArea中
taContent.setText(string);
// 每次输入结束,将输入框置空
tfTxt.setText("");
try {
System.out.println(s);
//不同每次都获取一次连接
// DataOutputStream dos=new DataOutputStream(s.getOutputStream());
dos.writeUTF(string);
dos.flush();
// dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} }
}
}

Server

 import java.io.DataInputStream;
import java.io.IOException;
import java.net.*; //server端
public class ChatServer { public static void main(String[] args) {
//服务器端是否已经启动
boolean started=false;
try {
ServerSocket ss = new ServerSocket(8888);
//服务器端启动以后,started=true
started=true;
//服务器端启动以后才能不断接收客户端的连接
while(started){
//定义boolean类型的变量,客户端时候建立连接
boolean bConnected;
Socket s=ss.accept();
System.out.println("a clint connected");
//客户端时候建立连接以后,bConnected=true;
bConnected=true;
DataInputStream dis=new DataInputStream(s.getInputStream());
//客户端建立连接以后,不断的接收写来的数据
while(bConnected){
String str=dis.readUTF();
System.out.println(str);
}
//没有连接上,关闭dis,释放资源
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} }

我们在这一版本中添加了连接标志,运行测试发现Server端可以接收多个客户端的连接,但是只能将第一个连接的客户端的输入显示在控制台,其他客户端依然无法获取到其他客户端的内容,仍会出现异常

版本0.9

ChatClient

 import java.awt.*;
import java.awt.event.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*;
public class ChatClient extends Frame { //暴露Socket
Socket s=null;
DataOutputStream dos=null;
TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 0.9
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
//窗口关闭的时候释放连接资源
disconnect();
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
connect();
}
//建立連接的方法
public void connect(){
try {
s=new Socket("127.0.0.1",8888);
dos=new DataOutputStream(s.getOutputStream());
System.out.println("connected!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void disconnect(){
try {
dos.close();
s.close();
} catch (Exception e) {
e.printStackTrace();
}
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
// 获得输入框的内容,并去除两端的空格
String string = tfTxt.getText().trim();
// 将获取的输入内容放置到TextArea中
taContent.setText(string);
// 每次输入结束,将输入框置空
tfTxt.setText("");
try {
System.out.println(s);
//不同每次都获取一次连接
// DataOutputStream dos=new DataOutputStream(s.getOutputStream());
dos.writeUTF(string);
dos.flush();
// dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} }
}
}

Server

 import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.*; //server端
public class ChatServer { public static void main(String[] args) {
//服务器端是否已经启动
boolean started=false;
ServerSocket ss = null;
Socket s=null;
DataInputStream dis=null;
try {
//可能会产生端口绑定异常
ss = new ServerSocket(8888);
}catch(BindException e){
System.out.println("端口使用中");
System.out.println("关闭相关程序,并重新运行");
System.exit(0);
}catch(IOException e){
e.printStackTrace();
}
try {
//服务器端启动以后,started=true
started=true;
//服务器端启动以后才能不断接收客户端的连接
while(started){
//定义boolean类型的变量,客户端时候建立连接
boolean bConnected;
s=ss.accept();
System.out.println("a clint connected");
//客户端时候建立连接以后,bConnected=true;
bConnected=true;
dis=new DataInputStream(s.getInputStream());
//客户端建立连接以后,不断的接收写来的数据
while(bConnected){
//readUTF是阻塞式的
String str=dis.readUTF();
System.out.println(str);
}
//没有连接上,关闭dis,释放资源
// dis.close();
}
//如果是因为客户端的关闭而导致的连接中断,则做这样的处理
}catch(EOFException e){
System.out.println("Client closed");
//其他异常,直接打印异常信息
}catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(dis!=null){
dis.close();
}
if(s!=null){
s.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
} }

之前的版本,当我们关闭客户端的时候都会发生异常,在这个版本中我们做了相应的处理,去捕获这个异常,并将连接资源的关闭工作进行细化;这里我们在客户端进行了处理:将DataOutputStream提到了全局变量的位置。不用每次都去获取一次Socket的输出流;

版本1.0

ChatClient

 import java.awt.*;
import java.awt.event.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*; public class ChatClient extends Frame { // 暴露Socket
Socket s = null;
DataOutputStream dos = null;
TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 1.0
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
// 窗口关闭的时候释放连接资源
disconnect();
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
connect();
} // 建立連接的方法
public void connect() {
try {
s = new Socket("127.0.0.1", 8888);
dos = new DataOutputStream(s.getOutputStream());
System.out.println("connected!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} public void disconnect() {
try {
dos.close();
s.close();
} catch (Exception e) {
e.printStackTrace();
}
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
// 获得输入框的内容,并去除两端的空格
String string = tfTxt.getText().trim();
// 将获取的输入内容放置到TextArea中
taContent.setText(string);
// 每次输入结束,将输入框置空
tfTxt.setText("");
try {
System.out.println(s);
// 不同每次都获取一次连接
// DataOutputStream dos=new
// DataOutputStream(s.getOutputStream());
dos.writeUTF(string);
dos.flush();
// dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} }
}
}

Server

 import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.*;
import java.security.Principal; //server端
public class ChatServer { boolean started = false;
ServerSocket ss = null; public static void main(String[] args) {
new ChatServer().start();
} public void start() { try {
// 可能会产生端口绑定异常
ss = new ServerSocket(8888);
// 服务器端启动以后,started=true
started = true;
} catch (BindException e) {
System.out.println("端口使用中");
System.out.println("关闭相关程序,并重新运行");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
try {
// 服务器端启动以后才能不断接收客户端的连接
while (started) {
Socket s = ss.accept();
// 不能在静态方法里new一个动态的类
Client c = new Client(s);
System.out.println("a clint connected");
new Thread(c).start();
}
// 如果是因为客户端的关闭而导致的连接中断,则做这样的处理
} catch (IOException e) {
e.printStackTrace(); } finally {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} } class Client implements Runnable {
private Socket s;
private DataInputStream dis = null;
private boolean bConnected = false; public Client(Socket s) {
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
bConnected = true;
}catch (IOException e) {
e.printStackTrace(); }
} @Override
public void run() {
try {
while (bConnected) {
String str = dis.readUTF();
System.out.println(str);
}
} catch (EOFException e) {
System.out.println("Client closed");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dis != null) {
dis.close();
}
if (s != null) {
s.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
} } }

版本1.0可以处理多个客户端同时连接并显示其输入内容的效果;将每个客户端封装到一个独立的线程中;

版本1.1

ChatClient

 import java.awt.*;
import java.awt.event.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*;
import java.util.*;
import java.util.List;
//转发给
public class ChatClient extends Frame { // 暴露Socket
Socket s = null;
DataOutputStream dos = null;
TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 1.1:转发给其他客户端,保存socket连接,用集合
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
// 窗口关闭的时候释放连接资源
disconnect();
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
connect();
} // 建立連接的方法
public void connect() {
try {
s = new Socket("127.0.0.1", 8888);
dos = new DataOutputStream(s.getOutputStream());
System.out.println("connected!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} public void disconnect() {
try {
dos.close();
s.close();
} catch (Exception e) {
e.printStackTrace();
}
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
// 获得输入框的内容,并去除两端的空格
String string = tfTxt.getText().trim();
// 将获取的输入内容放置到TextArea中
taContent.setText(string);
// 每次输入结束,将输入框置空
tfTxt.setText("");
try {
System.out.println(s);
// 不同每次都获取一次连接
// DataOutputStream dos=new
// DataOutputStream(s.getOutputStream());
dos.writeUTF(string);
dos.flush();
// dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} }
}
}

Server

 import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.*;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List; //server端
public class ChatServer { boolean started = false;
ServerSocket ss = null;
List<Client> clients = new ArrayList<Client>(); public static void main(String[] args) {
new ChatServer().start();
} public void start() { try {
// 可能会产生端口绑定异常
ss = new ServerSocket(8888);
// 服务器端启动以后,started=true
started = true;
} catch (BindException e) {
System.out.println("端口使用中");
System.out.println("关闭相关程序,并重新运行");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
try {
// 服务器端启动以后才能不断接收客户端的连接
while (started) {
Socket s = ss.accept();
// 不能在静态方法里new一个动态的类
Client c = new Client(s);
System.out.println("a clint connected");
new Thread(c).start();
clients.add(c);
}
// 如果是因为客户端的关闭而导致的连接中断,则做这样的处理
} catch (IOException e) {
e.printStackTrace(); } finally {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} class Client implements Runnable {
private Socket s;
private DataInputStream dis = null;
private DataOutputStream dos = null;
private boolean bConnected = false; public Client(Socket s) {
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
bConnected = true;
} catch (IOException e) {
e.printStackTrace(); }
} // 发送的方法
public void send(String str) {
try {
dos.writeUTF(str);
} catch (IOException e) {
e.printStackTrace();
}
} @Override
public void run() {
try {
while (bConnected) {
String str = dis.readUTF();
System.out.println(str);
for(int i=0;i<clients.size();i++){
Client c=clients.get(i);
c.send(str);
}
}
} catch (EOFException e) {
System.out.println("Client closed");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dis != null) {
dis.close();
}
if (s != null) {
s.close();
}
if(dos!=null){
dos.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
} }
}
}

将每个连接上的客户端Client添加到我们的List集合中,方便我们将我们的输入内容发送到List集合保存的Client;

版本1.2

ChatClient

 import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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.SocketException;
import java.net.UnknownHostException; //转发给
public class ChatClient extends Frame { // 暴露Socket
Socket s = null;
DataOutputStream dos = null;
DataInputStream dis = null;
private boolean bConnected = false;
TextField tfTxt = new TextField();
TextArea taContent = new TextArea(); Thread tRecv=new Thread(new RecvThread());
public static void main(String[] args) {
new ChatClient().launchFrame();
} /**
* Version 1.2
*/
public void launchFrame() {
// 设置Frame位置
setLocation(400, 300);
// 设置Frame大小
this.setSize(300, 300);
// 将输入框TextField加到Frame中,并放置到Frame布局的上面
add(tfTxt, BorderLayout.SOUTH);
// 将显示框TextArea加到Frame中,并放置到Frame布局的下面
add(taContent, BorderLayout.NORTH);
// 调整布局,处理多余空白框
pack();
// 添加窗口监听
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e) {
// 窗口关闭的时候释放连接资源
disconnect();
System.exit(0);
}
}); // 将监听器类TFListener添加到输入框TextField中
tfTxt.addActionListener(new TFListener());
// 窗口可见
setVisible(true);
connect(); tRecv.start();
} // 建立连接的方法
public void connect() {
try {
s = new Socket("127.0.0.1", 8888);
dos = new DataOutputStream(s.getOutputStream());
dis = new DataInputStream(s.getInputStream());
System.out.println("connected!");
bConnected = true;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} public void disconnect() {
try {
dos.close();
dis.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
/* try {
bConnected=false;
tRecv.join();
} catch(InterruptedException e){
e.printStackTrace();
} finally{
try {
dos.close();
dis.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
} }*/
} private class RecvThread implements Runnable { @Override
public void run() {
try {
while (bConnected) {
String str = dis.readUTF();
// System.out.println(str);
taContent.setText(taContent.getText()+str+"\n");
}
}catch(SocketException e){
System.out.println("退出!over");
}catch (IOException e) {
e.printStackTrace();
}
}
} // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
private class TFListener implements ActionListener { @Override
public void actionPerformed(ActionEvent e) {
// 获得输入框的内容,并去除两端的空格
String string = tfTxt.getText().trim();
// 将获取的输入内容放置到TextArea中
taContent.setText(string);
// 每次输入结束,将输入框置空
tfTxt.setText("");
try {
System.out.println(s);
// 不同每次都获取一次连接
// DataOutputStream dos=new
// DataOutputStream(s.getOutputStream());
dos.writeUTF(string);
dos.flush();
// dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} }
}
}

Server

 import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List; //server端
public class ChatServer { boolean started = false;
ServerSocket ss = null;
List<Client> clients = new ArrayList<Client>(); public static void main(String[] args) {
new ChatServer().start();
} public void start() { try {
// 可能会产生端口绑定异常
ss = new ServerSocket(8888);
// 服务器端启动以后,started=true
started = true;
} catch (BindException e) {
System.out.println("端口使用中");
System.out.println("关闭相关程序,并重新运行");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
try {
// 服务器端启动以后才能不断接收客户端的连接
while (started) {
Socket s = ss.accept();
// 不能在静态方法里new一个动态的类
Client c = new Client(s);
System.out.println("a clint connected");
new Thread(c).start();
clients.add(c);
}
// 如果是因为客户端的关闭而导致的连接中断,则做这样的处理
} catch (IOException e) {
e.printStackTrace(); } finally {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} class Client implements Runnable {
private Socket s;
private DataInputStream dis = null;
private DataOutputStream dos = null;
private boolean bConnected = false; public Client(Socket s) {
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
bConnected = true;
} catch (IOException e) {
e.printStackTrace(); }
} // 发送的方法
public void send(String str) {
try {
dos.writeUTF(str);
} catch (IOException e) {
e.printStackTrace();
}
} @Override
public void run() {
try {
while (bConnected) {
String str = dis.readUTF();
System.out.println(str);
for(int i=0;i<clients.size();i++){
Client c=clients.get(i);
c.send(str);
}
}
} catch (EOFException e) {
System.out.println("Client closed");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dis != null) {
dis.close();
}
if (s != null) {
s.close();
}
if(dos!=null){
dos.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
} }
}
}

此时我们已经可以实现我们的预期功能了,不同的客户端可以进行通信了;

版本1.3

ChatClient

 import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*; public class ChatClient extends Frame {
Socket s = null;
DataOutputStream dos = null;
DataInputStream dis = null;
private boolean bConnected = false; TextField tfTxt = new TextField(); TextArea taContent = new TextArea(); Thread tRecv = new Thread(new RecvThread()); public static void main(String[] args) {
new ChatClient().launchFrame();
} public void launchFrame() {
setLocation(400, 300);
this.setSize(300, 300);
add(tfTxt, BorderLayout.SOUTH);
add(taContent, BorderLayout.NORTH);
pack();
this.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent arg0) {
disconnect();
System.exit(0);
} });
tfTxt.addActionListener(new TFListener());
setVisible(true);
connect(); tRecv.start();
} public void connect() {
try {
s = new Socket("127.0.0.1", 8888);
dos = new DataOutputStream(s.getOutputStream());
dis = new DataInputStream(s.getInputStream());
System.out.println("connected!");
bConnected = true;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } public void disconnect() {
try {
dos.close();
dis.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
} /*
* try { bConnected = false; tRecv.join(); } catch(InterruptedException
* e) { e.printStackTrace(); } finally { try { dos.close(); dis.close();
* s.close(); } catch (IOException e) { e.printStackTrace(); } }
*/
} private class TFListener implements ActionListener { public void actionPerformed(ActionEvent e) {
String str = tfTxt.getText().trim();
// taContent.setText(str);
tfTxt.setText(""); try {
// System.out.println(s);
dos.writeUTF(str);
dos.flush();
// dos.close();
} catch (IOException e1) {
e1.printStackTrace();
} } } private class RecvThread implements Runnable { public void run() {
try {
while (bConnected) {
String str = dis.readUTF();
// System.out.println(str);
taContent.setText(taContent.getText() + str + '\n');
}
} catch (SocketException e) {
System.out.println("退出了,bye!");
} catch (EOFException e) {
System.out.println("推出了,bye - bye!");
} catch (IOException e) {
e.printStackTrace();
} } }
}

Server

 import java.io.*;
import java.net.*;
import java.util.*; public class ChatServer {
boolean started = false;
ServerSocket ss = null; List<Client> clients = new ArrayList<Client>(); public static void main(String[] args) {
new ChatServer().start();
} public void start() {
try {
ss = new ServerSocket(8888);
started = true;
} catch (BindException e) {
System.out.println("端口使用中....");
System.out.println("请关掉相关程序并重新运行服务器!");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
} try { while (started) {
Socket s = ss.accept();
Client c = new Client(s);
System.out.println("a client connected!");
new Thread(c).start();
clients.add(c);
// dis.close();
}
} catch (IOException 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 = null;
private DataOutputStream dos = null;
private boolean bConnected = false; public Client(Socket s) {
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
bConnected = true;
} catch (IOException e) {
e.printStackTrace();
}
} public void send(String str) {
try {
dos.writeUTF(str);
} catch (IOException e) {
clients.remove(this);
System.out.println("对方退出了!我从List里面去掉了!");
// e.printStackTrace();
}
} public void run() {
try {
while (bConnected) {
String str = dis.readUTF();
System.out.println(str);
for (int i = 0; i < clients.size(); i++) {
Client c = clients.get(i);
c.send(str);
// System.out.println(" a string send !");
}
/*
* for(Iterator<Client> it = clients.iterator();
* it.hasNext(); ) { Client c = it.next(); c.send(str); }
*/
/*
* Iterator<Client> it = clients.iterator();
* while(it.hasNext()) { Client c = it.next(); c.send(str);
* }
*/
}
} catch (EOFException e) {
System.out.println("Client closed!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dis != null)
dis.close();
if (dos != null)
dos.close();
if (s != null) {
s.close();
// s = null;
} } catch (IOException e1) {
e1.printStackTrace();
} }
} }
}

对程序出现的一些bug进行了修复,细化了关闭连接资源等操作,可以根据每个版本的差异梳理清楚代码的编写过程;

上一篇:Switch在swift中的使用


下一篇:Linux shell 的 test 命令用法详解