前言:
使用java来搭建一个简单的聊天室,参考作者—— java GUI 网络编程:图形界面聊天室。
服务端
新建一个项目,我们要写服务端。
在src下新建包——com.myserversocket.main,
然后在包下新建一个文件Server.java作为服务端的入口文件
Server.java的内容如下:
package com.myserversocket.main;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server{
public static void main(String[] args) {
try {
int localPort = 12223;
//建立服务器ServerSocket
ServerSocket ss = new ServerSocket(localPort);
//打印Server建立成功
System.out.println("Server on"
+ss.getInetAddress().getHostAddress()
+":"+localPort);
//监听端口,每当一个客户端连接就建立一个线程服务这个客户端
while(true){
//接收客户端的socket
Socket socket=ss.accept();
//提取客户端的ip和端口
String ip= socket.getInetAddress().getHostAddress();
int port=socket.getPort();
//建立新的服务端线程
//向该线程提供ServerSocket、Socket、客户端ip、客户端端口
new Thread(new ServerThread(socket,ss,ip,port)).start();
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
我们的思路很简单,来一个客户端,我们就给它分配一个线程为这个客户端服务。
接下来我们来写同包下的ServerThread.java
这是一个实现Runnable接口的类
package com.myserversocket.main;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
public class ServerThread implements Runnable {
//客户端的socket
Socket socket=null;
//服务器的ServerSocket
ServerSocket ss=null;
//获取的客户端IP
String ip=null;
//获取的客户端端口
int port=0;
//组合客户端的ip和端口字符串得到的uid字符串
String uid=null;
//静态ArrayList存储所有的uid,uid由ip和端口组成
static ArrayList<String> uid_arr=new ArrayList<String>();
//静态HashMap存储所有uid,ServerThread对象组成的键值对
static HashMap<String,ServerThread> hm=new HashMap<String,ServerThread>();
public ServerThread(Socket socket,ServerSocket ss,String ip,int port){
this.socket=socket;
this.ss=ss;
this.ip=ip;
this.port=port;
uid=ip+":"+port;
}
@Override
public void run() {
//将客户端uid存入ArrayList
uid_arr.add(uid);
//将当前uid和ServerThread对存入HashMap
hm.put(uid,this);
//时间显示模式
SimpleDateFormat sdf=new SimpleDateFormat("MM-dd hh:mm:ss");
//控制台打印客户端IP和端口
System.out.println("Client connected:"+uid);
try{
//获取输入流
InputStream in=socket.getInputStream();
//获取输出流
OutputStream out=socket.getOutputStream();
//向当前客户端传输连接成功的信息
String welcome=sdf.format(new Date())
+"\n成功连接服务器..."
+"\n服务器IP:"+ss.getInetAddress().getLocalHost()
+":"+ss.getLocalPort()
+"\n客户端IP:"+ip
+":"+port+"\n";
out.write(welcome.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
接下来我们可以用telnet连接本机的12223端口,就会看到输出,证明成功连接服务端。
下面还是ServerThread.java文件,我们实现全部的服务端代码,下面有的内容需要结合客户端才能理解,不理解先不要着急
完整的 ServerThread.java 代码
package com.myserversocket.main;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
public class ServerThread implements Runnable {
//客户端的socket
Socket socket=null;
//服务器的ServerSocket
ServerSocket ss=null;
//获取的客户端IP
String ip=null;
//获取的客户端端口
int port=0;
//组合客户端的ip和端口字符串得到的uid字符串
String uid=null;
//静态ArrayList存储所有的uid,uid由ip和端口组成
static ArrayList<String> uid_arr=new ArrayList<String>();
//静态HashMap存储所有uid,ServerThread对象组成的键值对
static HashMap<String,ServerThread> hm=new HashMap<String,ServerThread>();
public ServerThread(Socket socket,ServerSocket ss,String ip,int port){
this.socket=socket;
this.ss=ss;
this.ip=ip;
this.port=port;
uid=ip+":"+port;
}
@Override
public void run() {
//将客户端uid存入ArrayList
uid_arr.add(uid);
//将当前uid和ServerThread对存入HashMap
hm.put(uid,this);
//时间显示模式
SimpleDateFormat sdf=new SimpleDateFormat("MM-dd hh:mm:ss");
//控制台打印客户端IP和端口
System.out.println("Client connected:"+uid);
try{
//获取输入流
InputStream in=socket.getInputStream();
//获取输出流
OutputStream out=socket.getOutputStream();
//向当前客户端传输连接成功的信息
String welcome=sdf.format(new Date())
+"\n成功连接服务器..."
+"\n服务器IP:"+ss.getInetAddress().getLocalHost()
+":"+ss.getLocalPort()
+"\n客户端IP:"+ip
+":"+port+"\n";
out.write(welcome.getBytes());
//广播更新在线名单
updateOnLineList(out);
//准备缓冲区
byte[] buf=new byte[1024];
int len=0;
//持续监听并转发客户端消息
while(true){
//读取客户端输入
len = in.read(buf);
String msg=new String(buf,0,len);
System.out.println(msg);
//消息类型:退出或者聊天
String type=msg.substring(0,msg.indexOf("/"));
//消息本体
String content=msg.substring(msg.indexOf("/")+1);
//根据消息类型分别处理
//客户端要退出
if(type.equals("Exit")){
//更新ArrayList和HashMap,删除退出的uid和线程
uid_arr.remove(uid_arr.indexOf(uid));
hm.remove(uid);
//广播更新在线名单
updateOnLineList(out);
//控制台打印客户端IP和端口
System.out.println("Client Exited"+uid);
//结束循环(本线程即将结束)
break;
}else if(type.equals("Chat")){
//提取收信者地址
String[] receiver_arr=content.substring(0,
content.indexOf("/")).split(",");
//提取聊天内容
String word=content.substring(content.indexOf("/")+1);
//向收信者广播发出聊天信息
chatOnlineList(out,uid,receiver_arr,word);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
//向所有已连接的客户端更新在线名单
public void updateOnLineList(OutputStream out) {
try {
for (String tmp_uid : uid_arr) {
//获取广播收听者的输出流
//所有连接中的客户端的线程的客户端socket的输出流
out = hm.get(tmp_uid).socket.getOutputStream();
//将当前在线名单以逗号为分割组合成长字符串进行一次传送
StringBuilder sb=new StringBuilder("OnlineListUpdate/");
//给sb追加在线用户的uid,以逗号分隔
for(String member:uid_arr){
sb.append(member);
//以逗号分隔uid,除了最后一个
if(uid_arr.indexOf(member) != uid_arr.size()-1)
sb.append(",");
}
out.write(sb.toString().getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
//向指定的客户端发送聊天信息
public void chatOnlineList(OutputStream out,
String uid,
String[] receiver_arr,
String word){
try {
for (String tmp_uid : receiver_arr) {
//获取广播收听者的输出流
out = hm.get(tmp_uid).socket.getOutputStream();
//发送聊天信息
out.write(("Chat/" + uid + "/" + word).getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
知识点:
- 之前做Unity的SocketIO通信,服务端与客户端传的都是json格式的数据,而我们这里因为只是聊天工具,传的成了字符串,比如在线客户端的字符串:“OnlineListUpdate/1.1.1.1:1,2.2.2.2:2”、传输聊天内容:“Chat/1.1.1.1:1/asdflksjafkljlkdjsfak”,这样。
- String msg=new String(buf,0,len):创建String类对象的一种构造方法,取值是从第0个开始, 长度为len, 取的是 buf 数组
- msg.substring(0,msg.indexOf(“/”)):从零索引开始读取到/字符(不包括/),关于substring,可以参考菜鸟教程
服务端其实很简单,我们的重点是下面的客户端
客户端
新建一个项目,这个就是我们客户端的项目。
在src下新建两个包,com.myjavachatclient.main、com.myjavachatclient.view,
接着,在main下面新建一个java类,叫做 Client1.java,这个就是我们客户端的主类。
再在view下面新建一个java类,ClientFrame.java,这个主要负责编写GUI界面。
ClientFrame.java的内容如下
package com.myjavachatclient.view;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.text.SimpleDateFormat;
public class ClientFrame extends JFrame {
//时间显示格式
public SimpleDateFormat sdf=new SimpleDateFormat("MM-dd hh:mm:ss");
//窗口宽度
public final int WIDTH=700;
//窗口高度
public final int HEIGHT=700;
//创建发送按钮
JButton btnSend=new JButton("发送");
//创建清屏按钮
JButton btnClear=new JButton("清屏");
//创建退出按钮
JButton btnExit=new JButton("退出");
//创建消息接收者标签
JLabel lb1Receiver=new JLabel("右边选择聊天对象");
//创建文本输入框,参数分别为行数和列数(用来输入聊天信息)
JTextArea jtaSay=new JTextArea();
//创建聊天信息框(用来显示聊天记录)
JTextArea jtaChat=new JTextArea();
//当前在线列表的列标题
String[] colTitles={"网名","IP","端口"};
//当前在线列表的数据
String[][] rowData=null;
//创建当前在线列表
public JTable jtbOnline=new JTable(
new DefaultTableModel(rowData,colTitles){
//表格不可编辑,只可以显示
//构造这个对象时重写isCellEditable方法
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}
);
//创建聊天信息框的滚动窗(通过jtaChat)
JScrollPane jspChat=new JScrollPane(jtaChat);
//创建当前在线列表的滚动窗
JScrollPane jspOnline=new JScrollPane(jtbOnline);
//设置默认窗口属性,连接窗口组件
public ClientFrame(){
//标题
this.setTitle("蝙蝠通讯");
//大小
this.setSize(WIDTH,HEIGHT);
//不可缩放
this.setResizable(false);
//设置布局,不适用默认布局,完全自定义
this.setLayout(null);
//设置按钮大小和位置
addButton(btnSend,20,600,100,60,
new Font("黑体", Font.BOLD,18));
addButton(btnClear,140,600,100,60,
new Font("黑体", Font.BOLD,18));
addButton(btnExit,260,600,100,60,
new Font("黑体", Font.BOLD,18));
//设置标签大小和位置
lb1Receiver.setBounds(20,420,300,30);
//添加标签
this.add(lb1Receiver);
//添加文本输入框
//设置文本输入框大小和位置
jtaSay.setBounds(20,460,360,120);
//设置文本输入框字体
jtaSay.setFont(new Font("楷体",Font.BOLD,16));
//添加文本输入框
this.add(jtaSay);
//聊天消息框自动换行
jtaChat.setLineWrap(true);
//聊天消息框不可编辑,只可显示
jtaChat.setEditable(false);
//设置聊天消息框字体
jtaChat.setFont(new Font("楷体",Font.BOLD,16));
//设置聊天框滚动区(内容就是聊天消息文字域)
addScrollPane(jspChat,20,20,360,400);
//设置在线用户滚动区(内容就是在线用户文字域)
addScrollPane(jspOnline,420,20,250,400);
}
private void addButton(JButton btn,int x,int y,
int width,int height,Font f){
btn.setBounds(x,y,width,height);
btn.setFont(f);
//添加按钮
this.add(btn);
}
private void addScrollPane(JScrollPane jsp,int x,int y,
int width,int height){
//设置滚动窗的水平滚动条属性:不可见
jsp.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//设置滚动窗的垂直滚动条属性:需要时自动出现
jsp.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
//设置滚动窗的大小和位置
jsp.setBounds(x,y,width,height);
//添加滚动窗
this.add(jsp);
}
}
Client1.java中,我们在主函数中调用
ClientFrame cframe=new ClientFrame();
cframe.setVisible(true);
即可看到大致GUI布局
设计好GUI界面后,我们来到Client1,实现通信功能
还记得我们在服务端中有这么一句吗?
//向当前客户端传输连接成功的信息
String welcome=sdf.format(new Date())
+"\n成功连接服务器..."
+"\n服务器IP:"
+ss.getInetAddress().getLocalHost()
+":"+ss.getLocalPort()
+"\n客户端IP:"+ip +":"+port+"\n";
out.write(welcome.getBytes());
我们现在在客户端的Client1.java 写上如下代码,就可以成功接收这段信息
package com.myjavachatclient.main;
import com.myjavachatclient.view.ClientFrame;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client1 {
//连接服务端的IP和端口
static String ip="127.0.0.1";
static int port=12223;
//建立客户端Socket
public static Socket socket=null;
//消息接收者uid(本客户端)
public static StringBuilder uidReceiver=null;
public static void main(String[] args){
//创建客户端窗口对象
ClientFrame cframe=new ClientFrame();
//窗口关闭键无效,必须通过退出键退出客户端以便善后
cframe.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//获得本机屏幕横向分辨率
int w= Toolkit.getDefaultToolkit().getScreenSize().width;
//获得本机屏幕纵向分辨率
int h= Toolkit.getDefaultToolkit().getScreenSize().height;
//将窗口置中
cframe.setLocation((w-cframe.WIDTH)/2,(h-cframe.HEIGHT)/2);
//设置客户端窗口为可见
cframe.setVisible(true);
try{
//连接服务器
socket=new Socket(ip,port);
//获取输入流
InputStream in=socket.getInputStream();
//获取输出流
OutputStream out=socket.getOutputStream();
//定义一个缓冲区 获取服务端欢迎信息
byte[] buf=new byte[1024];
int len=in.read(buf);
//将欢迎信息打印在聊天消息框内
cframe.jtaChat.append(new String(buf,0,len));
cframe.jtaChat.append("\n");
}catch (Exception e) {
cframe.jtaChat.append("服务器挂了.....\n");
e.printStackTrace();
}
}
}
开启服务端,再开启客户端测试一下吧!
可见已经成功连接,下面我们就来实现聊天功能
最终Client1.java的全部代码
package com.myjavachatclient.main;
import com.myjavachatclient.view.ClientFrame;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
public class Client1 {
//连接服务端的IP和端口
static String ip="127.0.0.1";
static int port=12223;
//建立客户端Socket
public static Socket socket=null;
//消息接收者uid(本客户端)
public static StringBuilder uidReceiver=null;
public static void main(String[] args){
//创建客户端窗口对象
ClientFrame cframe=new ClientFrame();
//窗口关闭键无效,必须通过退出键退出客户端以便善后
cframe.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//获得本机屏幕横向分辨率
int w= Toolkit.getDefaultToolkit().getScreenSize().width;
//获得本机屏幕纵向分辨率
int h= Toolkit.getDefaultToolkit().getScreenSize().height;
//将窗口置中
cframe.setLocation((w-cframe.WIDTH)/2,(h-cframe.HEIGHT)/2);
//设置客户端窗口为可见
cframe.setVisible(true);
try{
//连接服务器
socket=new Socket(ip,port);
//获取输入流
InputStream in=socket.getInputStream();
//获取输出流
OutputStream out=socket.getOutputStream();
//定义一个缓冲区 获取服务端欢迎信息
byte[] buf=new byte[1024];
int len=in.read(buf);
//将欢迎信息打印在聊天消息框内
cframe.jtaChat.append(new String(buf,0,len));
cframe.jtaChat.append("\n");
//持续等待接收服务器信息直至退出
while(true){
in=socket.getInputStream();
len=in.read(buf);
System.out.println(len);
//处理服务器传来的信息
String msg=new String(buf,0,len);
//消息类型
String type = msg.substring(0, msg.indexOf("/"));
//消息本体:更新后的名单或者聊天内容
String content = msg.substring(msg.indexOf("/") + 1);
//根据消息类型分别处理
//更新在线名单
if(type.equals("OnlineListUpdate")){
//提取在线列表的数据模型
DefaultTableModel tbm=(DefaultTableModel)cframe
.jtbOnline.getModel();
//清楚在线名单列表
tbm.setRowCount(0);
//更新在线名单
String[] onlinelist=content.split(",");
//逐一添加到当前在线者
for(String member:onlinelist){
String[] tmp=new String[3];
//如果是自己则不在名单中显示
if(member.equals(socket.getInetAddress().getHostAddress()
+":"+socket.getLocalPort())){
continue;
}
tmp[0]="";
tmp[1]=member.substring(0,member.indexOf(":"));
tmp[2]=member.substring(member.indexOf(":")+1);
//添加当前在线者之一
tbm.addRow(tmp);
}
//提取在线列表的渲染模型
DefaultTableCellRenderer tbr = new DefaultTableCellRenderer();
//表格数据居中显示
tbr.setHorizontalAlignment(JLabel.CENTER);
cframe.jtbOnline.setDefaultRenderer(Object.class,tbr);
}else if(type.equals("Chat")){
//得到发送者和发送内容
String sender=content.substring(0,content.indexOf("/"));
String word=content.substring(content.indexOf("/")+1);
//在聊天显示窗展示聊天信息
cframe.jtaChat.append(cframe.sdf.format(new Date())
+"\n来自 "+sender+":\n"
+word+"\n\n");
//展示最新消息
cframe.jtaChat.setCaretPosition(cframe.jtaChat
.getDocument().getLength());
}
}
}catch (Exception e) {
cframe.jtaChat.append("服务器挂了.....\n");
e.printStackTrace();
}
}
}
分析服务端代码与目前客户端代码可知,完成数据通讯最后就差一个客户端向服务端发送“Chat/……”信息的功能,这就要给我们的ClientFrame.java中的按钮注册响应事件了。
客户端完整代码
Client1.java
package com.myjavachatclient.main;
import com.myjavachatclient.view.ClientFrame;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
public class Client1 {
//连接服务端的IP和端口
static String ip="127.0.0.1";
static int port=12223;
//建立客户端Socket
public static Socket socket=null;
//消息接收者uid(本客户端)
public static StringBuilder uidReceiver=null;
public static void main(String[] args){
//创建客户端窗口对象
ClientFrame cframe=new ClientFrame();
//窗口关闭键无效,必须通过退出键退出客户端以便善后
cframe.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//获得本机屏幕横向分辨率
int w= Toolkit.getDefaultToolkit().getScreenSize().width;
//获得本机屏幕纵向分辨率
int h= Toolkit.getDefaultToolkit().getScreenSize().height;
//将窗口置中
cframe.setLocation((w-cframe.WIDTH)/2,(h-cframe.HEIGHT)/2);
//设置客户端窗口为可见
cframe.setVisible(true);
try{
//连接服务器
socket=new Socket(ip,port);
//获取输入流
InputStream in=socket.getInputStream();
//获取输出流
OutputStream out=socket.getOutputStream();
//定义一个缓冲区 获取服务端欢迎信息
byte[] buf=new byte[1024];
int len=in.read(buf);
//将欢迎信息打印在聊天消息框内
cframe.jtaChat.append(new String(buf,0,len));
cframe.jtaChat.append("\n");
//持续等待接收服务器信息直至退出
while(true){
in=socket.getInputStream();
len=in.read(buf);
System.out.println(len);
//处理服务器传来的信息
String msg=new String(buf,0,len);
//消息类型
String type = msg.substring(0, msg.indexOf("/"));
//消息本体:更新后的名单或者聊天内容
String content = msg.substring(msg.indexOf("/") + 1);
//根据消息类型分别处理
//更新在线名单
if(type.equals("OnlineListUpdate")){
//提取在线列表的数据模型
DefaultTableModel tbm=(DefaultTableModel)cframe
.jtbOnline.getModel();
//清楚在线名单列表
tbm.setRowCount(0);
//更新在线名单
String[] onlinelist=content.split(",");
//逐一添加到当前在线者
for(String member:onlinelist){
String[] tmp=new String[3];
//如果是自己则不在名单中显示
if(member.equals(socket.getInetAddress().getHostAddress()
+":"+socket.getLocalPort())){
continue;
}
tmp[0]="";
tmp[1]=member.substring(0,member.indexOf(":"));
tmp[2]=member.substring(member.indexOf(":")+1);
//添加当前在线者之一
tbm.addRow(tmp);
}
//提取在线列表的渲染模型
DefaultTableCellRenderer tbr = new DefaultTableCellRenderer();
//表格数据居中显示
tbr.setHorizontalAlignment(JLabel.CENTER);
cframe.jtbOnline.setDefaultRenderer(Object.class,tbr);
}else if(type.equals("Chat")){
//得到发送者和发送内容
String sender=content.substring(0,content.indexOf("/"));
String word=content.substring(content.indexOf("/")+1);
//在聊天显示窗展示聊天信息
cframe.jtaChat.append(cframe.sdf.format(new Date())
+"\n来自 "+sender+":\n"
+word+"\n\n");
//展示最新消息
cframe.jtaChat.setCaretPosition(cframe.jtaChat
.getDocument().getLength());
}
}
}catch (Exception e) {
cframe.jtaChat.append("服务器挂了.....\n");
e.printStackTrace();
}
}
}
ClientFrame.java
package com.myjavachatclient.view;
import com.myjavachatclient.main.Client1;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ClientFrame extends JFrame {
//时间显示格式
public SimpleDateFormat sdf=new SimpleDateFormat("MM-dd hh:mm:ss");
//窗口宽度
public final int WIDTH=700;
//窗口高度
public final int HEIGHT=700;
//创建发送按钮
JButton btnSend=new JButton("发送");
//创建清屏按钮
JButton btnClear=new JButton("清屏");
//创建退出按钮
JButton btnExit=new JButton("退出");
//创建消息接收者标签
JLabel lb1Receiver=new JLabel("右边选择聊天对象");
//创建文本输入框,参数分别为行数和列数(用来输入聊天信息)
public JTextArea jtaSay=new JTextArea();
//创建聊天信息框(用来显示聊天记录)
public JTextArea jtaChat=new JTextArea();
//当前在线列表的列标题
String[] colTitles={"网名","IP","端口"};
//当前在线列表的数据
String[][] rowData=null;
//创建当前在线列表
public JTable jtbOnline=new JTable(
new DefaultTableModel(rowData,colTitles){
//表格不可编辑,只可以显示
//构造这个对象时重写isCellEditable方法
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}
);
//创建聊天信息框的滚动窗(通过jtaChat)
JScrollPane jspChat=new JScrollPane(jtaChat);
//创建当前在线列表的滚动窗
JScrollPane jspOnline=new JScrollPane(jtbOnline);
//设置默认窗口属性,连接窗口组件
public ClientFrame(){
//标题
this.setTitle("蝙蝠通讯");
//大小
this.setSize(WIDTH,HEIGHT);
//不可缩放
this.setResizable(false);
//设置布局,不适用默认布局,完全自定义
this.setLayout(null);
//设置按钮大小和位置
addButton(btnSend,20,600,100,60,
new Font("黑体", Font.BOLD,18));
addButton(btnClear,140,600,100,60,
new Font("黑体", Font.BOLD,18));
addButton(btnExit,260,600,100,60,
new Font("黑体", Font.BOLD,18));
//设置标签大小和位置
lb1Receiver.setBounds(20,420,300,30);
//添加标签
this.add(lb1Receiver);
//添加文本输入框
//设置文本输入框大小和位置
jtaSay.setBounds(20,460,360,120);
//设置文本输入框字体
jtaSay.setFont(new Font("楷体",Font.BOLD,16));
//添加文本输入框
this.add(jtaSay);
//聊天消息框自动换行
jtaChat.setLineWrap(true);
//聊天消息框不可编辑,只可显示
jtaChat.setEditable(false);
//设置聊天消息框字体
jtaChat.setFont(new Font("楷体",Font.BOLD,16));
//设置聊天框滚动区(内容就是聊天消息文字域)
addScrollPane(jspChat,20,20,360,400);
//设置在线用户滚动区(内容就是在线用户文字域)
addScrollPane(jspOnline,420,20,250,400);
//添加按钮们的监听方法
addListenerOfBtnSend();
addListenerOfBtnClear();
addListenerOfBtnExit();
addListenerOfOnlineUser();
}
private void addButton(JButton btn,int x,int y,int width,int height,Font f){
btn.setBounds(x,y,width,height);
btn.setFont(f);
//添加按钮
this.add(btn);
}
private void addScrollPane(JScrollPane jsp,int x,int y,int width,int height){
//设置滚动窗的水平滚动条属性:不可见
jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//设置滚动窗的垂直滚动条属性:需要时自动出现
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
//设置滚动窗的大小和位置
jsp.setBounds(x,y,width,height);
//添加滚动窗
this.add(jsp);
}
private void addListenerOfBtnSend(){
btnSend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//显示最新消息
jtaChat.setCaretPosition(jtaChat
.getDocument()
.getLength());
try{
//有收信人才发送
if(Client1.uidReceiver.toString()
.equals("")==false){
//在聊天消息窗打印发送动作信息
jtaChat.append(sdf.format(new Date())
+"\n发往 "+Client1.uidReceiver.toString()
+":\n");
//显示要发送的信息(输入区内容复制给显示区域)
jtaChat.append(jtaSay.getText()+"\n\n");
//向服务端发送聊天信息
OutputStream out=Client1.socket.getOutputStream();
out.write(("Chat/"+Client1.uidReceiver.toString()
+"/"+jtaSay.getText()).getBytes());
}
} catch (IOException ex) {
ex.printStackTrace();
}finally{
//文本输入框清除
jtaSay.setText("");
}
}
});
}
//注册清屏按钮的响应事件
private void addListenerOfBtnClear(){
btnClear.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//聊天框清屏
jtaChat.setText("");
}
}
);
}
//添加退出按钮的响应事件
private void addListenerOfBtnExit(){
btnExit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try{
//向服务器发送信息
OutputStream out=Client1.socket.getOutputStream();
out.write("Exit/".getBytes());
//退出
System.exit(0);
}catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
//添加在线列表的选择的响应事件(通过MouseListener接口)
private void addListenerOfOnlineUser(){
jtbOnline.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
//获取在线列表的数据模型
DefaultTableModel tbm=(DefaultTableModel)jtbOnline.getModel();
//提取鼠标选中的行作为消息目标,最少一个人,最多全体在线者接受消息
int[] selectedIndex=jtbOnline.getSelectedRows();
//将所有消息目标的uid拼接成一个字符串,以逗号分隔
StringBuilder uidRec=new StringBuilder("");
for(int i=0;i<selectedIndex.length;i++){
uidRec.append((String)tbm.getValueAt(
selectedIndex[i],1));
uidRec.append(":");
uidRec.append((String)tbm.getValueAt(
selectedIndex[i],2));
if(i!=selectedIndex.length-1){
uidRec.append(",");
}
}
//将uidRec赋值给静态的uidRecever
Client1.uidReceiver=uidRec;
//修改标签
lb1Receiver.setText("发送给:"
+Client1.uidReceiver.toString());
}
@Override
public void mousePressed(MouseEvent e){}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
});
}
}
最后效果:
基础功能就实现了,如果有我还想法的话,会在升级一下这个项目,感谢阅读。
商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢