import java.awt.*;
import java.awt.Event;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.*;
import java.net.*;
import javax.swing.*;
//服务器与客户端最根本的区别就是建立连接那里,其他都一样
public abstract class TestJavaChatServer {
public static void main(String[] args) {
// TODO Auto-generated method stub
new ChatServer().launch();
}
}
//这里定义一个类完成主方法中的代码(窗口化界面的建立,相应的初始化和相应的方法类调用之类),使得主方法尽量的简洁
class ChatServer{
//这里将需要用到的文本框,按钮,类的对象都定义成属性,可以减少后续代码;
private TextArea ta=null;
private TextField tf=null;
private Button bn=null;
private DataOutputStream dos=null;
private DataInputStream dis=null;
private ServerSocket ss=null;
private Socket s=null;
private Frame f=null;
public void launch(){
//该方法完成四个工作:创建图像界面UI;创建与客户端连接;启动服务器端的读取线程;启动服务器端的写入线程
creatUI();
connection();
//这里定义两个线程,是因为读写是两个线程进行不能用单线程完成
new TCPServerReader().start();
new TCPServerWriter().start();
}
public void creatUI(){
f=new Frame();
f.setTitle("服务器端");
ta =new TextArea();//这个为显示的文本区域
tf=new TextField();//这个为输入的文本框
//先定义一个Panel对象P而且布局管理器为BorderLayout即东西南北布局
Panel P=new Panel(new BorderLayout());
bn=new Button("发送");//这里定义一个发送按钮
P.add(tf,BorderLayout.CENTER);//往P中添加输入文本框tf,放在中间
P.add(bn,BorderLayout.EAST);//发生按钮放在东边
f.add(ta,BorderLayout.CENTER);//在f中添加一个文本区域ta放在中间
f.add(P,BorderLayout.SOUTH);//而南边放P,即发送文本框和发送按钮都放到最底边
f.addWindowListener(new actionB());//增加窗口控制事件监听器
f.setSize(250, 250);//设置窗口大小
f.setVisible(true);
}
//这里是关闭的方法,对输入输出流和接受发送端的关闭异常捕获
public void close()
{
try{
dos.close();
dis.close();
s.close();
ss.close();
}
catch(Exception e)
{
e.printStackTrace();
System.exit(-1);
}
}
//建立连接方法,最主要的就是创建ServerSocket对象,然后调用accept方法返回给一个Socket对象,后面就是明确Socket对象s调用方法getInputStream和getOutputStream方法的返回值分别作为new DataInputStream和DataOutstream对象的参数
public void connection(){
try{
//创建服务器端
ss=new ServerSocket(8888);
s=ss.accept();//调用accept方法后才开始不断监听客户端是否有发送连接请求,并且返回一个Socket对象,所以需要用一个Socket对象s来接收
//这里主要用到DataInputStream和DataOutputStream类中的读取和写入基本类型的方法
dis=new DataInputStream(s.getInputStream());
dos=new DataOutputStream(s.getOutputStream());
}
catch(Exception e)
{
e.printStackTrace();
System.out.println("建立连接失败");
System.exit(-1);
}
}
class TCPServerReader extends Thread{
public void run(){
//while1的死循环确保不在特定条件下保持通话的进行
while(true)
{
try{
String str=dis.readUTF();//定义一个String对象str用来接收dis.readUTF方法返回的内容
tf.setText("");//setText让文本框显示信息
ta.append("对方说:"+str+"\n");
if(str.equalsIgnoreCase("再见")||str.equalsIgnoreCase("88"))
{
close();//这里是调用close方法将Socket,ServerSocket,DataInputStream和DataOutputStream的对象都close了
System.exit(-1);
}
}
catch(Exception e)
{
JOptionPane.showMessageDialog(f, "已经断开连接");//这里是swing里面的类方法
return;
}
}
}
}
//发送数据的线程,这里需要事件处理即事件监听器,因为要涉及到写入数据并且点发送键发送出去
class TCPServerWriter extends Thread{
public void run(){
//第一个事件监听器是检测文本输入的事件
tf.addActionListener(new TCPListener());
//第二事件监听器是检测按钮发送的事件
bn.addActionListener(new TCPListener());
}
}
class TCPListener implements ActionListener{
public void actionPerformed(ActionEvent e){
try{
String str=tf.getText();//定义一个String类型用来接受文本框tf使用getText方法获取键盘输入的内容
tf.setText("");//这里再次把文本框设置为空,即清空,以便于下次输入
ta.append("回复:"+str+"\n");//文本区域调用append方法获取str内容并显示出来
dos.writeUTF(str);//这里就是dos调用WriteUTF把str写进去,便于传输
//这里若双方输入再见或者88,然后调用String的equalsIgnoreCase方法进行对比,该对较方法只比较内容
if(str.equalsIgnoreCase("再见")||str.equalsIgnoreCase("88")){
close();
System.exit(-1);//若满足则关闭对话框
}
}
catch(Exception e2){
JOptionPane.showMessageDialog(f, "已经断开连接");//f是对话框,内容为断开连接
return;
}
}
}
}
class actionB implements WindowListener //这里是定义一个实现窗口事件监听的类即add后面类名为事件监听器
{
//下面是对窗口监听类中方法的重写,因为借口中的方法都是抽象的,所以使用时都需要重写,下面是java api文档中窗口事件监听器WindowListener中的相关方法
//下面的方法是相应的事件操作,方法名可在API文档中发现
public void windowClosing(WindowEvent e)//关闭
{
System.exit(0);
}
public void windowOpened(WindowEvent e){};
public void windowIconified(WindowEvent e){};//最小化
public void windowDeiconified(WindowEvent e){}; //最大化
public void windowClosed(WindowEvent e){};
public void windowActivated(WindowEvent e){};
public void windowDeactivated(WindowEvent e){};
}
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.*;
import java.net.*;
import javax.swing.*;
public class TCPJavaChatClient {
public static void main(String[] args) {
// TODO Auto-generated method stub
new TCPChatClient().launch();
}
}
class TCPChatClient {
private Frame f=null;
private TextArea ta=null;
private TextField tf=null;
private Button bn=null;
private DataInputStream dis=null;
private DataOutputStream dos=null;
private Socket s=null;
public void launch(){
creatUI();
connection();
new TCPClientReader().start();
new TCPClientWriter().start();
}
public void creatUI(){
f=new Frame();
f.setTitle("客户端");
tf=new TextField();
ta=new TextArea();
Panel p=new Panel(new BorderLayout());
bn=new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.addWindowListener(new actionA());
f.setSize(250,250);
f.setVisible(true);
}
public void close()
{
try{
s.close();
dis.close();
dos.close();
}
catch(Exception e)
{
System.exit(-1);
}
}
public void connection()
{
try{
//这里一旦new Socket成功后,便自动的请求建立连接,参数为目标IP地址和目标端口号
s=new Socket("192.168.157.55",8888);
//这里是Socket对象调用getInputStream方法获取输入数据,然后用DataInputStream类的对象来接收
dis=new DataInputStream(s.getInputStream());
//这里Socket对象调用getOutputStream方法来写入数据,然后用DataOutputStream类的对象来接收
dos=new DataOutputStream(s.getOutputStream());
}
catch(Exception e)
{
System.out.println("建立连接失败");
e.printStackTrace();
System.exit(-1);
}
}
class TCPClientReader extends Thread{
public void run()
{
while(true)
{
try{
String str=dis.readUTF();
tf.setText("");
ta.append("对方说:"+str+"\n");
if(str.equalsIgnoreCase("再见")||str.equalsIgnoreCase("88"))
{
System.exit(-1);
}
}
catch(Exception e)
{
JOptionPane.showMessageDialog(f, "已经断开连接");
return;
}
}
}
}
class TCPClientWriter extends Thread{
public void run()
{
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
}
}
class TCPClientListener implements ActionListener{
public void actionPerformed(ActionEvent e)
{
try{
String str=tf.getText();//获取从键盘输入到文本框Text里的内容放到str
tf.setText("");//设置文本内容为“”,即为空,没进入一次循环就将文本框里面的内容清空,可以再次输入
ta.append("回复:"+str+"\n");//这里是文本区域调用append方法将输入文本框str内容添加进去,即显示出来
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见")||str.equalsIgnoreCase("88"))
{
System.exit(-1);
}
}
catch(Exception e2)
{
JOptionPane.showMessageDialog(f, "已经断开连接");
return;
}
}
}
}
class actionA implements WindowListener //这里是定义一个实现窗口事件监听的类即add后面类名为事件监听器
{
//下面是对窗口监听类中方法的重写,因为借口中的方法都是抽象的,所以使用时都需要重写,下面是java api文档中窗口事件监听器WindowListener中的相关方法
//下面的方法是相应的事件操作,方法名可在API文档中发现
public void windowClosing(WindowEvent e)//关闭
{
System.exit(0);
}
public void windowOpened(WindowEvent e){};
public void windowIconified(WindowEvent e){};//最小化
public void windowDeiconified(WindowEvent e){}; //最大化
public void windowClosed(WindowEvent e){};
public void windowActivated(WindowEvent e){};
public void windowDeactivated(WindowEvent e){};
}
尽量都给出了程序注释,最后运行结果如下: