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

最新文章

  1. Webpack从入门到上线
  2. --关于null在oracle数据库中是否参与计算,进行验证,
  3. IEnumerable&lt;T&gt;与IQueryable&lt;T&gt;以及.net的扩展方法
  4. js(ext)中,设置[!!异步!!]上传的简单进度条
  5. Sqlserver中关于锁
  6. C++ 我想这样用(一)
  7. poj 1696 Space Ant(模拟+叉积)
  8. TableView_编辑 实例代码
  9. POJ 1955 Rubik&#39;s Cube
  10. JDBC之事务隔离级别以及ACID特性
  11. HDU 1589 Find The Most Comfortable Road 最小生成树+枚举
  12. Mybatis批量插入、批量更新
  13. 持续交付之软件包管理maven篇
  14. 【转载】大白话Docker入门(二)
  15. thinkpad yoga 12 / thinkpad s1 yoga / WS860
  16. 51Nod 最小公倍数之和V3
  17. [LeetCode] 78. Subsets tag: backtracking
  18. IE 浏览器的兼容性列表设置
  19. hdu5184 数论证明
  20. 80.Vigenère密码(模拟)

热门文章

  1. Atheros AR9485 ubuntu 10.04 驱动安装及networking disable问题解决
  2. Generic Data Access Layer泛型的数据访问层
  3. Protected Member Access
  4. Android开发之权限列表
  5. MySQL select into 和 SQL select into
  6. 5 commands to check memory usage on Linux
  7. 你用哪种工具进行iOS app自动化功能测试?
  8. UVa 11542 (高斯消元 异或方程组) Square
  9. HDU 1425 sort 【哈希入门】
  10. 苹果官方 Crash文件分析方法 (iOS系统Crash文件分析方法)