java swing+socket实现多人聊天程序
swing+socket实现多人聊天程序
1.准备工作
先看效果:

- 客户端项目结构图:

- 服务端项目结构图:

2.运行原理
- 服务端
先开一个线程serverListerner,线程中开启一个Serversocket
用Serversocket.accept()监听指定端口
一旦有socket连接进来,就为该socket开启一个线程,用于读取该socket输入流的信息,一旦有信息,就通知其他客户端
并将线程保存到Vector<ChartThread> 集合内,交给ChatThreadManager管理,主要是处理分发消息
- 客户端:
点击connect按钮和服务端建立连接,客户端会开启线程随时监听socket的输入流
服务端accept后就会为该socket建立线程,监控该socket的流信息
当客户端点击发送按钮时,将文本框内的信息,写进socket中,此时服务端的线程抓到了socket的输入流信息,就让ChatThreadManager管理类,去通知集合内其他socket,为多个socket写入相同的信息,那么其余的客户端就收到消息了,实现了多人聊天的功能
3.核心代码
- 服务端:
StartServer.java
Main函数入口,实例ServerListener类,并开启该线程
package com.chart;
public class StartServer
{
public static void main(String[] args)
{
new ServerListener().start();
}
}
ServerListener.java:
用Serversocket.accept()监听指定端口,一旦有socket连接进来,就为该socket开启一个ChatThread线程
并将ChatThread线程保存到Vector<ChartThread> 集合内,交给ChatThreadManager管理,
package com.chart;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JOptionPane;
public class ServerListener extends Thread{
public void run()
{
try {
ServerSocket serversocket= new ServerSocket(23456);
while(true) {
Socket socket=serversocket.accept(); //进程会阻塞在这句,直到有socket连接进来,就往下执行
System.out.println("one client has connected");
ChatThread cs=new ChatThread(socket);
cs.start();
ChatThreadManager.getChartManager().add(cs);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
ChatThread:
在此线程中监听客户端socket的来信 并调用publish()方法,通知ChatThreadManager类
package com.chart;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;
public class ChatThread extends Thread {
Socket socket;
public ChatThread(Socket s) {
this.socket = s;
}
public void out(String out) {
try {
socket.getOutputStream().write(out.getBytes("UTF-8"));
} catch (Exception e) {
}
}
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String line = "";
while ((line = br.readLine()) != null) {
System.out.println(line);
ChatThreadManager.getChartManager().publish(this, line);
}
br.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
ChatThreadManager.java
单例类,构造函数私有,管理多个ChatThread, 用于通知其他客户端
package com.chart;
import java.util.Vector;
public class ChatThreadManager {
private ChatThreadManager() {
}
private static final ChatThreadManager Cm = new ChatThreadManager();
public static ChatThreadManager getChartManager() {
return Cm;
}
Vector<ChatThread> ChatSocket_vector = new Vector<ChatThread>();
public void add(ChatThread cs) {
ChatSocket_vector.add(cs);
}
public void publish(ChatThread cs, String msg) {
for (int i = 0; i < ChatSocket_vector.size(); i++) {
ChatThread csTemp = ChatSocket_vector.get(i);
if (!cs.equals(csTemp)) {
csTemp.out(msg + "\n");
}
}
}
}
- 客户端:
StartClient.java:
main 函数入口,新建主窗体,并显示
package com.client;
import java.awt.EventQueue;
import view.MyClientWindow;
public class StartClient {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
MyClientWindow frame=new MyClientWindow();
frame.setVisible(true);
ConnectionManager.getChatManager().setWindow(frame);
}
catch (Exception e) {
}
}
});
}
}
MyClientWindow.java:
继承JFrame,布局窗体,connect按钮执行建立连接事件,send按钮则往socket中写入发送信息
package view;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.border.EmptyBorder;
import com.client.ConnectionManager;
public class MyClientWindow extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel contentPane;
private JTextArea txt;
private JTextField txtip;
private JTextField txtSend;
public MyClientWindow() {
setAlwaysOnTop(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
txt = new JTextArea();
txt.setText("准备...");
txtip = new JTextField();
txtip.setText("120.254.12.102");
txtip.setColumns(10);
JButton btnConnect = new JButton("connect");
btnConnect.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
ConnectionManager.getChatManager().connect(txtip.getText());
}
});
txtSend = new JTextField();
txtSend.setText("hello");
txtSend.setColumns(10);
JButton btnSend = new JButton("send");
btnSend.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
ConnectionManager.getChatManager().send(txtSend.getText());
appendText("我说: " + txtSend.getText());
txtSend.setText("");
}
});
GroupLayout gl_contentPane = new GroupLayout(contentPane);
gl_contentPane.setHorizontalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(
Alignment.TRAILING,
gl_contentPane.createSequentialGroup().addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING)
.addGroup(gl_contentPane.createSequentialGroup()
.addComponent(txtSend, GroupLayout.DEFAULT_SIZE, 325, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btnSend, GroupLayout.PREFERRED_SIZE, 109, GroupLayout.PREFERRED_SIZE))
.addGroup(Alignment.LEADING,
gl_contentPane.createSequentialGroup()
.addComponent(txtip, GroupLayout.PREFERRED_SIZE, 294,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btnConnect, GroupLayout.DEFAULT_SIZE, 140, Short.MAX_VALUE))
.addComponent(txt, GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE)).addContainerGap()));
gl_contentPane.setVerticalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE)
.addComponent(txtip, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addComponent(btnConnect))
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(txt, GroupLayout.DEFAULT_SIZE, 198, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING).addComponent(btnSend)
.addComponent(txtSend, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE))));
contentPane.setLayout(gl_contentPane);
}
/* 客户端发送的内容添加到中间的txt控件中 */
public void appendText(String in) {
txt.append("\n" + in);
}
}
ConnectionManager.java
package com.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import view.MyClientWindow;
public class ConnectionManager {
private ConnectionManager() {
}
private static final ConnectionManager instance = new ConnectionManager();
public static ConnectionManager getChatManager() {
return instance;
}
MyClientWindow window;// 为了能在界面上显示服务器发来的信息,就需要传一个MainWindow的引用进来
Socket socket;
private String IP;
BufferedReader bReader;
PrintWriter pWriter;
public void setWindow(MyClientWindow window) {
this.window = window;
}
public void connect(String ip) {
this.IP = ip;
new Thread() {
@Override
public void run() {
// 实现网络方法
try {
socket = new Socket(IP, 23456);
// 输出流
pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
// 输入流
bReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
// 如果读取数据为空
while ((line = bReader.readLine()) != null) {
window.appendText("收到: " + line);
}
// 读完数据之后要关闭
pWriter.close();
bReader.close();
pWriter = null;
bReader = null;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
public void send(String sendMsg) {
if (pWriter != null) {
pWriter.write(sendMsg + "\n");
pWriter.flush();
} else {
window.appendText("当前链接已经中断...");
}
}
}
4.运行服务端程序:
1.把MainServerPro服务端程序导出成jar包
2.用xshell把jar包上传到linux
3.程序选用了23456端口,因为部署在阿里云的服务器上,所以要打开阿里云的端口限制

4.关闭centos7的防火墙
# service firewalld status; #查看防火墙状态
(disabled 表明 已经禁止开启启动 enable 表示开机自启,inactive 表示防火墙关闭状态 activated(running)表示为开启状态)
# service firewalld start; #开启防火墙
# service firewalld stop; #关闭防火墙
5.启动服务,运行MainServerPro.jar
有以下三种方式,选择第二种:java -jar xxxxx.jar &
java -jar xxxxx.jar // 当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出
java -jar xxxxx.jar & //当前ssh窗口不被锁定,但是当窗口关闭时,程序中止运行。
nohup Java -jar xxxxxx.jar & //不挂断运行命令,当账户退出或终端关闭时,程序仍然运行
6.最后启动客户端,进行聊天
在点击connect的时候出现报错:
Exception in thread "Thread-0" java.awt.HeadlessException: No X11 DISPLAY variable was set, but this program performed an operation which requires it.
原因是因为服务端程序中有一句JOptionPane.showMessageDialog(null, "one client has connect to 12345 port");
在linux这种不支持图形界面的环境中 调用图形界面swing有关的代码时,会抛出异常。。。
解决方法很粗暴,直接把这句代码删掉
5.源代码下载
https://download.csdn.net/download/wcc27857285/10784144
java swing+socket实现多人聊天程序的更多相关文章
- Java使用socket实现两人聊天对话
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; /* ...
- Java网络编程以及简单的聊天程序
网络编程技术是互联网技术中的主流编程技术之一,懂的一些基本的操作是非常必要的.这章主要讲解网络编程,UDP和Socket编程,以及使用Socket做一个简单的聊天软件. 全部代码下载:链接 1.网络编 ...
- java socket之多人聊天室Demo
一.功能介绍 该功能实现了一个类似QQ的最简单多人聊天室,如下图所示. 二.目录结构 三.服务端 1)SocketServer类,该类是服务端的主类,主要负责创建聊天窗口,创建监听客户端的线程: pa ...
- 多线程+socket实现多人聊天室
最近在学习多线程的时候打算做一个简单的多线程socke聊天的程序,结果发现网上的代码都没有完整的实现功能,所以自己实现了一个demo: demo功能大致就是,有一个服务端负责信息转发,多个客户端发送消 ...
- Android IPC机制(五)用Socket实现跨进程聊天程序
1.Socket简介 Socket也称作“套接字“,是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信.它分为流式套接字和数据包套接 ...
- 基于Socket实现多人聊天室
当前支持: 1.仅文字 2.加入聊天室提醒 3.退出聊天室提醒 可能出现的BUG: 1.可能出现客户端发送信息后不能及时推送,需要下一个客户端发送信息后一起推送 服务端代码: 1 package co ...
- java-网络通信--socket实现多人聊天(基于命令行)
先编写最简答的服务器 思路 1编写一个实现Runnable接口的静态内部类 ServerC,便于区分每个客户端 1.1 获取客户端数据函数 public String remsg() 1.2 转发消息 ...
- Java 多线程Socket编程通讯--实现聊天室代码
1.创建服务器类 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import ja ...
- java版两人聊天程序
server.java import java.io.*; import java.net.*; import java.text.SimpleDateFormat; import java.util ...
随机推荐
- 在.net core上,Web网站调用微信支付-统一下单接口(xml传参)一直返回错误:mch_id参数格式错误
这是 微信支付-统一下单 接口文档 一.问题描述 在调用统一下单接口时,报mch_id参数格式错误,但商户ID确实是10位数字正确的,可就是一直报这个错误 返回的错误xml如下: 二.排错过程 1.多 ...
- 利用bu命令下延迟断点
bu可以针对符号下断点.这里是用bu下延迟断点的意义在于即使目标驱动没有被加载,windbg也允许我们针对符号设置断点.当新加载驱动程序后,windbg就会检查驱动程序中是否包含了设置了延迟断点的函数 ...
- 云-腾讯云-云通信:云通信(IM)
ylbtech-云-腾讯云-云通信:云通信(IM) 企业数字化转型的通信助手,让通信触达全球,智联万物 1.返回顶部 1. 云通信(Instant Messaging,IM)承载亿级 QQ 用户即时通 ...
- Spring Boot 发布 jar 包转为 war 包秘籍。
Spring Boot是支持发布jar包和war的,但它推荐的是使用jar形式发布.使用jar包比较方便,但如果是频繁修改更新的项目,需要打补丁包,那这么大的jar包上传都是问题.所以,jar包不一定 ...
- Python对象和类
Python 里的所有数据都是以对象形式存在的,对象是类的实例. 定义类(class) 使用class来定义一个类. 比如,定义一个cat类,如下: class Cat(): def __init__ ...
- 如何将数组2对象中的属性push进数组1的对象中去,组合成新的数组
- java实践经验几种常见数据库连接池的使用比较
经历的几个产品及项目中,包括了各种数据库及应用服务器,基本上几种常见的数据库连接池都用到了,根据使用的情况把这些连接池比较一下吧.(http://m.0834jl.com) 感觉在介绍之前有必要阐述一 ...
- 【转载】objective-c强引用与弱引用
形象比喻蛮好玩的^_^ __weak 和 __strong 会出现在声明中 默认情况下,一个指针都会使用 __strong 属性,表明这是一个强引用.这意味着,只要引用存在,对象就不能被销毁 ...
- linux mint & ubuntu 安装QQ国际版
ubuntu安装QQ相对简单 下载qq国际版deb文件,直接安装即可. 下载地址: http://pan.baidu.com/s/1hqmYzlU 下面就重点说一下linux mint 安装qq. 1 ...
- vue实现分环境打包步骤(给不同的环境配置相对应的打包命令)
在新建好的项目中,一般执行npm run build就是打包了,但此时只能打包到一个环境,不同环境需要配置不同的地址,可以手动更改接口的地址,也可以自行配置命令而不需要每次打包进行地址切换,步骤如下: ...