简易聊天器----JavaTCP原理实现

简易网络聊天器----JavaTcp原理实现

点击这里,到码云获取我的项目

涉及知识

  1. Java的网络编程tcp(暂仅局域网下实现)
  2. Java IO操作
  3. Java 多线程(可实现多人同时在线聊天)
  4. Java图形化界面(图形化操作)

运行截图

服务器启动界面

在这里插入图片描述

客户端启动界面(支持多线程,多人在线)

在这里插入图片描述
程序运行截图

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

代码演示

服务器端代码

package com.tcp;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import java.io.*;

//Java Tcp_app :  Server
public class Server {
	//主程序开始的地方
	public static void main(String[] args) throws Exception{
		new Oper();		//通过Oper类的构造方法开始
	}
	
}

//Oper类, SocketServer服务器运行的类
class Oper{
	public static boolean flag = false;			//定义服务器的启动按钮, 初始化为未启动
	public static int count = -1;				//定义客户端数量(以数组下标表示个数)
	ServerLogin sl;								//定义登录窗口对象
	ServerChatWindows scw;						//定义聊天窗口对象
    ServerSocket server;						//服务器
	Socket client;								//客户端									
	public Oper() throws Exception{				//Oper类构造方法
		sl = new ServerLogin();									//创建显示登录窗口,根据用户操作选择是否启动服务器
		while(!this.flag) {System.out.println("");}				//用户若无操作, 程序进入死循环等待状态
		sl.jf.dispose();
		scw = new ServerChatWindows();							//关闭登录窗口, 产生聊天窗口
		server = new ServerSocket(8090);						//建立服务器, 指定端口号为 8888
		//进入死循环, 不断接受客户端的连接, 利用多线程实现多个客户端的连接
		while(true) {
			client = server.accept();							//等待客户端连接
			new Thread(new ServerThread(client, scw)).start();	//开辟服务器对此客户端的一个线程,将此客户端和服务器聊天界面传入,并开始运行此线程
			count++;											//客户端数量累加
		}
	}
}


//服务器对应客户端的多线程类, 客户端连接的数量就是服务器线程启动的数量
class ServerThread implements Runnable{
	Socket client;								//定义客户端
	ServerChatWindows scw;						//服务器聊天窗口
	JTextField jtf_news;						//定义发送信息的文本框
	JTextArea jta_mess;							//定义不可编辑的文本区域作为聊天记录界面
	JButton jbt_send;							//定义发送按钮
	PrintStream out;							//定义客户端输出流
	BufferedReader buf;							//定义客户端输入流
	public static String str;					//定义全局变量 str 为接收客户端发送的信息
	//此类的构造方法, 接收客户端 和 服务器的固定聊天界面
	public ServerThread(Socket client, ServerChatWindows scw) {
		this.client = client;
		this.scw = scw;
		this.jtf_news = scw.jtf_news;
		this.jbt_send = scw.jbt_send;
		this.jta_mess = scw.jta_mess;			//进行对象的复制传输
	}
	//线程的run()方法
	public void run() {
		try {
			buf = new BufferedReader(new InputStreamReader(client.getInputStream()));	//得到此客户端的输入流
			out = new PrintStream(client.getOutputStream());							//得到此客户端的输出流
			this.jbt_send.addActionListener(new ServerSendNews(this.out, this.scw));	//增加服务器端信息发送的按钮监听器
			this.jtf_news.addKeyListener(new ServerSendNews(this.out, this.scw));		//增加服务器端信息发送的键盘监听器,回车键为发送
			//进入死循环, 不断得到客户端发送的信息
			while(true) {
				this.str = null;					//初始化信息为 null
				str = buf.readLine();				//得到客户端发送的信息
				this.jbt_send.doClick();			//模拟点击服务器信息发送的按钮, 将客户端发送的信息回显给所有客户端
			}
		}catch(Exception e) {						//当客户端断开连接后或其它原因的异常处理提醒
			System.out.println("Client isn't connected!");
		}
	}
}


//服务器启动界面窗口
class ServerLogin{
	JFrame jf;								//定义窗口
	Container con;							//存放组件的容器
	JPanel jpl_jlb;							
	JPanel jpl_jbt; 						//定义两个存放组件的二级容器
	JLabel jlb;								//定义服务器信息标签
	JButton jbt_on;							
	JButton jbt_ce;							//定义两个按钮,作为登录和取消键
	//通过构造方法生成聊天界面窗口
	public ServerLogin() throws Exception {
		jf = new JFrame("Chat-Room Server");		//登录窗口窗口上栏信息
		con = jf.getContentPane();
		jpl_jlb = new JPanel();
		jpl_jbt = new JPanel();
		
		jlb = new JLabel("Chat--Server");			//标签信息
		jbt_on = new JButton("On");					//登录按钮
		jbt_ce = new JButton("Off");				//取消按钮
		
		//为登录按纽增加事件监听器, 当用户点击 on 时, Oper类全局变量 flag变为 ture, 表示服务器启动, Oper类启动服务器
		jbt_on.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				Oper.flag = true;
			}
		});
		
		//为取消按钮增加事件监听器, 当用户点击 off 时退出程序
		jbt_ce.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		
		//向容器增加组件
		
		jpl_jlb.add(jlb);
		jpl_jbt.add(jbt_on);
		jpl_jbt.add(jbt_ce);
		
		con.add(jpl_jlb, BorderLayout.NORTH);
		con.add(jpl_jbt, BorderLayout.SOUTH);
		
		//设置窗体属性
		jf.setSize(300, 120);			//设置大小
		jf.setLocation(600, 260);		//设置位置
		jf.setVisible(true);			//设置可见
		jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);			//设置可关闭
	}
}


//服务器聊天界面窗口类
class ServerChatWindows{
	JFrame jf;							//定义窗口
	Container con;						//定义容器
	JPanel send;
	JPanel mess;						//二级容器
	JTextField jtf_news;
	JTextArea jta_mess;
	JScrollPane jsp;
	JButton jbt_send;					//文本域 , 文本框, 按钮等组件
	BufferedReader buf;					//定义输入流
	PrintStream out;					//定义输出流
	//通过构造方法生成 服务器聊天界面
	public ServerChatWindows() throws Exception{
		jf = new JFrame("Server");
		con = jf.getContentPane();
		jtf_news = new JTextField("Hello, I'm Server!", 14);
		jta_mess = new JTextArea(15, 25);
        jta_mess.setLineWrap(true);
		jsp = new JScrollPane(jta_mess, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

		jta_mess.setEditable(false);		//多行文本域设置为不可编辑, 用来存放聊天记录
		jbt_send = new JButton("send");
		
		mess = new JPanel();
		send = new JPanel();
		
		send.add(jtf_news);
		send.add(jbt_send);
		
		con.add(jsp, BorderLayout.CENTER);
		con.add(send, BorderLayout.SOUTH);
		
		jf.pack();
//		jf.setSize(280, 340);
		jf.setLocation(500, 260);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);		//如上, 设置聊天窗口的属性
		
	}
}


//定义按钮及键盘监听器, 监听服务器发送给的内容
class ServerSendNews implements ActionListener, KeyListener{
	private static int count = -1;		//定义发送信息的次数
	private ServerChatWindows scw;		//定义聊天服务器窗口
	private JTextArea jta_mess;			//定义存放聊天记录的文本框
	private JTextField jtf_news;		//信息发送的文本框
	private JButton jbt_send;			//发送按钮
	private PrintStream out;			//得到向客户端的输出流
	private boolean flag;				//判断数据流为回显客户端的信息或者为服务器发送的信息
	private String str;					//存储信息的字符串
	//构造方法, 得到客户端的输出流和服务器的聊天窗口
	public ServerSendNews(PrintStream out, ServerChatWindows scw) throws Exception{
		this.out = out;
		this.jtf_news = scw.jtf_news;
		this.jta_mess = scw.jta_mess;
		this.jbt_send = scw.jbt_send;			//对象的复制传输
	}
	//按钮发生动作需执行的事件
	public void actionPerformed(ActionEvent e) {
		this.flag = true;					//初始化flag为true, 即服务器发送的信息
		//若ServerThread类的str属性信息不为空, 则此动作为 模拟点击按钮动作, 即回显客户端信息, flag变为 false, 得到客户端信息
		if(ServerThread.str != null){
			this.str = ServerThread.str;
			this.flag = false;
		}
		else this.str = "Server: " + jtf_news.getText();		//否则,则为服务器所发送的信息
		out.println(this.str);									//输出此信息至其它客户端
		this.count++;											//输出信息次数累加
		if((this.count+1) % (Oper.count+1) == 0) {				//若输出信息次数可以整除客户端数量, 服务器的信息文本域也将变动, 消息记录也累加
			if(this.flag == true) jtf_news.setText("");			//若为服务器发送的信息, 则清空服务器的信息发送文本域
			this.jta_mess.append(this.str + "\n");				//累加消息记录
		}
	}
	//增加键盘监听器事件, 当用户输入信息时输入回车键, 执行此事件
	//同上, 此为服务器所发送信息, 无需判断信息流
	public void keyPressed(KeyEvent arg0) {
		if(arg0.getKeyCode() == KeyEvent.VK_ENTER){
			this.str = "Server: " + jtf_news.getText();
			out.println(this.str);
			this.count++;
			if((this.count+1) % (Oper.count+1) == 0) {
				jtf_news.setText("");
				this.jta_mess.append(this.str + "\n");
			}
		}
	}
	public void keyReleased(KeyEvent arg0) {}
	public void keyTyped(KeyEvent arg0) {}
}

客户端代码

package com.tcp;

import java.awt.BorderLayout;


import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import java.net.*;
import java.io.*;
import java.util.*;

//Java Tcp: Client
public class Client {
	//程序开始的位置
	public static void main(String[] args) throws Exception{
		new OperSocket();
	}

}


//Tcp Client客户端处理的总工作台
class OperSocket{
	public static boolean flag = false;						//定义全局变量,客户端启动的总开关, 初始值为false,表示未开
	private Socket client;									//定义客户端
	private BufferedReader buf;								//定义客户端输入流
	private PrintStream out;								//定义客户端输出流
	private MyLoginWindows mlw;								//定义客户端登录窗口
	private ChatWindows cw;									//定义客户端聊天窗口
	private JTextArea jta_mess;								//定义客户端聊天记录文本框
	private JButton jbt_send;								//定义客户端聊天信息发送按钮
	private JTextField jtf_news;							//定义客户端聊天信息的文本框
	private String str;										//定义 str 接收服务器发送的信息
	private String name;									//定义 name 为客户端输入的 user name
	//通过构造方法, 开始运行
	public OperSocket() throws Exception{
		mlw = new MyLoginWindows();							//产生用户登录窗口
		while(!this.flag) {System.out.print("");}			//程序进入死循环, 等待用户登录
		this.client = new Socket("127.0.0.1", 8090);		//若登录成功, 输入连接服务器主机的ip地址(此127.0.0.1为测试ip)
        this.name = mlw.jtf_user.getText();					//对象的复制传输
		mlw.jf.dispose();									//关闭登录界面窗口
		cw = new ChatWindows(this.name);					//生成客户端聊天界面窗口
		this.jta_mess = cw.jta_mess;
		this.jbt_send = cw.jbt_send;
		this.jtf_news = cw.jtf_news;
		this.buf = new BufferedReader(new InputStreamReader(client.getInputStream()));		//获取此客户端的输入流
		this.out = new PrintStream(client.getOutputStream());								//得到此客户端的输出流
		jbt_send.addActionListener(new SendNews(this.out, this.jtf_news, this.jta_mess, this.name));	//为发送按钮增加监听器
		jtf_news.addKeyListener(new SendNews(this.out, this.jtf_news, this.jta_mess, this.name));	//为发送文本框增加键盘监听器
		//客户端进入死循环等待服务器发送的信息内容
		while(true) {
			str = this.buf.readLine();			//获取服务器发送的信息
			this.jta_mess.append(str + "\n");	//将信息添加到客户端聊天窗口的消息记录中
		}										
	}
}


//MyLoginWindows   定义客户端登录界面窗口类
//客户端登录界面窗口同服务器近似
class MyLoginWindows{
	JFrame jf;
	JButton jbt_lg;
	JButton jbt_ce;
	JLabel jlb_user;
	JLabel jlb_pass;
	JTextField jtf_user;
	JPasswordField jpf_pass;
	JPanel user;
	JPanel pass;
	JPanel button;
	Container con;
	public MyLoginWindows() {
		jf = new JFrame("Login Windows");
		
		jlb_user = new JLabel("User:");
		jlb_pass = new JLabel("Pass:");
		jtf_user = new JTextField(14);
		jtf_user.setText("your name:");				//设置客户端用户姓名提示
		jpf_pass = new JPasswordField(14);
		jbt_lg = new JButton("Login");
		jbt_ce = new JButton("Cancel");
		
		//增加事件监听器, 若用户点击登录, 则进行客户端启动开关为 true, 客户端启动
		jbt_lg.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				OperSocket.flag = true;
			}
		});
		
		//增加事件监听器, 若用户点击取消, 则退出程序
		jbt_ce.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		
		//对登录界面窗口组件布局进行设置
		
		con = jf.getContentPane();
		
		user = new JPanel();
		pass = new JPanel();
		button = new JPanel();
		
		con.add(user, BorderLayout.NORTH);
		con.add(pass, BorderLayout.CENTER);
		con.add(button, BorderLayout.SOUTH);
//		con.setBackground(Color.RED);
		
		user.add(jlb_user);
		user.add(jtf_user);
		pass.add(jlb_pass);
		pass.add(jpf_pass);
		button.add(jbt_lg);
		button.add(jbt_ce);
		
//		jf.pack();
		jf.setSize(360, 150);
		jf.setLocation(600, 280);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);
	}
}


//ChatWindows 为客户端 聊天界面窗口
//同服务器聊天界面窗口近似
class ChatWindows{
	JFrame jf;
	Container con;
	JPanel send;
	JPanel mess;
	JTextField jtf_news;
	JTextArea jta_mess;
	JScrollPane jsp;
	JButton jbt_send;
	Socket user;
	InetAddress inet;
	Scanner sc;
	BufferedReader buf;
	PrintStream out;
	String str;
	public ChatWindows(String s) throws Exception{
		
		jf = new JFrame(s);
		con = jf.getContentPane();
		jtf_news = new JTextField(s + "   is Login!", 14);
		jta_mess = new JTextArea(15, 25);
		jta_mess.setLineWrap(true);
		jsp = new JScrollPane(jta_mess, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
		
        jta_mess.setEditable(false);
		jbt_send = new JButton("Send");
		mess = new JPanel();
		send = new JPanel();
		
		send.add(jtf_news);
		send.add(jbt_send);
		
		con.add(jsp, BorderLayout.CENTER);
		con.add(send, BorderLayout.SOUTH);
		
		jf.pack();
//		jf.setSize(280, 340);
		jf.setLocation(500, 260);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);
		
	}	
}


//定义按钮及键盘监听器类, 对客户端输入信息操作进行监听
class SendNews implements ActionListener, KeyListener{
	private JTextArea jta_mess;
	private JTextField jtf_news;
	private Socket user;
	private PrintStream out;
	private BufferedReader buf;
	private String str;
	private String name;
	public SendNews(PrintStream out, JTextField jtf_news, JTextArea jta_mess, String name) throws Exception{
		this.out = out;
		this.jta_mess = jta_mess;
		this.jtf_news = jtf_news;
		this.name = name;
	}
	//按钮事件的动作事件操作
	public void actionPerformed(ActionEvent e) {
		str = jtf_news.getText();						//获取客户端信息流内容
		out.println(this.name + ":  " + str + "");		//将此信息输出到服务器端
		jtf_news.setText("");							//客户端信息文本清空
	}
	//当用户在信息发送文本域   按回车键   的事件处理
	public void keyPressed(KeyEvent arg0) {
		if(arg0.getKeyCode() == KeyEvent.VK_ENTER){		//若事件源为键盘的回车
			str = jtf_news.getText();					//同上, 进行输出信息操作
			out.println(this.name + ":  " + str + "");
			jtf_news.setText("");
		}
	}
	public void keyReleased(KeyEvent arg0) {}
	public void keyTyped(KeyEvent arg0) {}
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gaolw1102

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值