今天学习了一下简单聊天程序(类似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进行了修复,细化了关闭连接资源等操作,可以根据每个版本的差异梳理清楚代码的编写过程;

JavaSE聊天室的更多相关文章

  1. JavaSE项目之聊天室

    引子: 当前,互联网 体系结构的参考模型主要有两种,一种是OSI参考模型,另一种是TCP/IP参考模型. 一.OSI参考模型,即开放式通信系统互联参考模型(OSI/RM,Open Systems In ...

  2. 学习JavaSE TCP/IP协议与搭建简易聊天室

    一.TCP/IP协议 1.TCP/IP协议包括TCP.IP和UDP等 2.域名通过dns服务器转换为IP地址 3.局域网可以通过IP或者主机地址寻找到相应的主机 4.TCP是可靠的连接,效率低,且连接 ...

  3. JavaSE项目之聊天室swing版

    引子: 当前,互联网 体系结构的参考模型主要有两种,一种是OSI参考模型,另一种是TCP/IP参考模型. 一.OSI参考模型,即开放式通信系统互联参考模型(OSI/RM,Open Systems In ...

  4. Java网络编程案例---聊天室

    网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java.net包中JavaSE的API包含有类和接口,它们提供低层次的通信细节.你可以直接使用这些类和接口,来专注于解决 ...

  5. 介于JAVAswing和Socket写的聊天室

    在厦门的第一阶段给我们复习了JAVASE基础,第一阶段的小玩具叫我们自选题材,我自己选了聊天室这个内容,这个小玩具无论是线程,还是网络编程,都会涉及到,比较有综合性,所以我选了这个: 这是我的包体结构 ...

  6. 利用Node.js的Net模块实现一个命令行多人聊天室

    1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...

  7. php+websocket搭建简易聊天室实践

    1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...

  8. 基于select的python聊天室程序

    python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...

  9. 用SignalR 2.0开发客服系统[系列2:实现聊天室]

    前言 交流群:195866844 上周发表了 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 这篇文章,得到了很多帮助和鼓励,小弟在此真心的感谢大家的支持.. 这周继续系列2,实现聊天室 ...

随机推荐

  1. 写Java程序要体现面向对象

          对于之前写的一篇文章现在想想存在不足之处,之前写的测试ArrayList和LinkedList的各项操作性能比较的程序没有体现面向对象的封装特性,所以,今天把代码重新写了一遍,其实改动的地 ...

  2. Hadoop1.x与Hadoop2的区别

    转自:http://blog.csdn.net/fenglibing/article/details/32916445 六.Hadoop1.x与Hadoop2的区别 1.变更介绍 Hadoop2相比较 ...

  3. LinuxShell算术运算

    Bash shell 的算术运算有四种方式:1:使用 expr 外部程式 加法 r=`expr 4 + 5`echo $r注意! '4' '+' '5' 这三者之间要有空白r=`expr 4 * 5` ...

  4. tc 2014 college tour 250 500

    题意: You are given a long long n. Return the largest divisor of n that is a perfect square. That is, ...

  5. struct TABLE

    struct TABLE { TABLE() {} /* Remove gcc warning */ TABLE_SHARE *s; handler *file; TABLE *next, *prev ...

  6. UVALive 5713 Qin Shi Huang's National Road System(次小生成树)

    题意:对于已知的网络构建道路,使城市两两之间能够互相到达.其中一条道路是可以免费修建的,问需要修建的总长度B与免费修建的道路所连接的两城市的人口之和A的比值A/B最大是多少. 因为是求A/B的最大值, ...

  7. codeforces 333A - Secrets

    题意:保证不能正好配齐n,要求输出可以用的最大硬币数. 注意如果用到某种硬币,那么这种硬币就有无穷多个.所以11=3+3+3+3,12=9+9,13=3+3+3+3+3 #include<cst ...

  8. UVa 11729 Commando War 突击战

    你有 n 个部下,每个部下需要完成一个任务.第 i 个部下需要你花 Bi 分钟交待任务,然后他会立刻独立地.无间断地执行 Ji 分钟后完成任务.你需要选择交待任务的顺序,使得所有任务尽早执行完毕(即最 ...

  9. 【Mysql】安装 mysql-5.7.5 指南

    因为同学需要安装mysql,安装过程,一路百度,在这里记录一下步奏.以后还会用到. 1.mysql-5.7.5-m15-winx64.zip下载 官方网站下载地址:http://cdn.mysql.c ...

  10. SQL Server 2005的XML数据修改语言(XML DML)

    转:http://www.microsoft.com/china/msdn/library/data/sqlserver/XMLDML.mspx?mfr=true 作为对XQuery语言的扩展,XML ...