1.1 主程序入口

在主程序入口处,通过设置MyWindow的第一个参数,如果为true则为服务器,如果为false,则为客户端,当然也可以设置第二个参数,区分客户端和服务器的窗口标题。

public class JavaMain {

    public static void main(String[] args) {
MyWindow w=new MyWindow(false,"QQ聊天");  //运行时将false改成true, 先启动服务端,然后再改成false启动客户端
w.setNet("192.168.1.103", 12345);
}
}

1.2 界面程序

界面程序根据主程序传来的参数不同而创建客户端和服务器窗口,根据界面的构造函数中第一个参数,isServer设置服务器窗体或者是客户端窗体。

public class MyWindow extends JFrame {
private static final long serialVersionUID = 1L;
// 定义一个成员变量
Client myClient = null;
Server myServer=null;
JTextArea area=null; // 设置默认的IP地址和端口
private String ipAddress="127.0.0.1";
private int nPort=50000; private boolean isServer=false; // 构造函数
public MyWindow(boolean isServer,String title) {
this.isServer=isServer;
setTitle(title);
setSize(300, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
initComponents();
setVisible(true);
} // 为外界提供设置IP地址和端口的方法
public void setNet(String ip,int port){
ipAddress=ip;
nPort=port; // 对通信接口初始化
initCommication();
} // 通过构造函数,将端口和文本显示区传递给myServer对象
public void initCommication(){
if (isServer) {
myServer=new Server(nPort,area);
}else{
myClient=new Client(ipAddress,nPort,area);
}
} public void initComponents() {
// 添加一个文本区
area = new JTextArea();
area.setBounds(10, 20, 260, 200);
add(area);
// 添加一个文本框
final JTextField text = new JTextField();
text.setBounds(10, 240, 260, 30);
add(text);
// 添加一个发送按钮
JButton button = new JButton("发送");
button.setBounds(10, 290, 80, 30); button.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
if (isServer) {
myServer.sendServerMsg(text.getText());
}else{
myClient.sendMsg(text.getText());
}
}
});
add(button);
}
}

1.3 服务器类

服务器类比较复杂,这里用到了内部类,内部类的好处就是能随时访问外部类的成员和方法,而无需通过传参数的方法达到目的。

// 监听主线程
public class Server extends Thread {
// 服务器Socket
private ServerSocket serverSocket = null;
private ArrayList<ServerThread> clientList = null; // 显示区
private JTextArea jTextArea = null; // 构造函数
public Server(int port, JTextArea area) {
jTextArea = area;
try {
// 开始绑定端口
serverSocket = new ServerSocket(port);
// 初始化客户端连接的列表
clientList = new ArrayList<ServerThread>();
} catch (IOException e) {
System.err.println("服务器端口初始化失败!\n");
}
jTextArea.setText("服务器成功启动,等待客户连接!\n");
start(); // 启动线程
} public void sendServerMsg(String msg) {
jTextArea.append(msg + "\n");
for (int i = clientList.size() - 1; i >= 0; i--) {
clientList.get(i).getWriter().println("服务器:" + msg);
clientList.get(i).getWriter().flush();
}
} // 线程程序
public void run() {
while (true) {
try {
// 用阻塞的方式,等待用户连接请求
Socket socket = serverSocket.accept();
// 启动一条为客户端服务的线程
ServerThread svthread = new ServerThread(socket);
svthread.start();
// 将该客户加入列表中
clientList.add(svthread); } catch (IOException e) {
e.printStackTrace();
}
}
} // 服务线程(内部类),用于处理客户端的服务线程
class ServerThread extends Thread {
// 当前正在连接的Socket
Socket socket = null;
// 当前连接的Socket的输入和输出流(数据出入口)
private PrintWriter writer = null;
private BufferedReader reader = null; // 构造函数
public ServerThread(Socket s) {
socket = s;
try {
// 获取输入输出流
reader = new BufferedReader(new InputStreamReader(
s.getInputStream()));
writer = new PrintWriter(s.getOutputStream());
// 在此可以写接收用户端的信息,解析出来(IP地址) } catch (Exception e) {
e.printStackTrace();
}
} // 获得输入流,供外界调用
public BufferedReader getReader() {
return reader;
} // 获得输出流,供外界调用
public PrintWriter getWriter() {
return writer;
} // 获得socket
public Socket getSocket() {
return socket;
} // 线程服务程序
public void run() {
// 创建一个变量,用于接收客户端发来的信息
String message = null;
while (true) {
try {
// 读取输入流
message = reader.readLine();
// 如果是下线命令
if (message.equals("Bye")) {
// 在客户端列表上删除该用户
ServerThread temp = null;
for (int i = clientList.size() - 1; i >= 0; i--) {
temp = clientList.get(i);
if (temp.getSocket().equals(socket)) {
clientList.remove(i);
}
temp.stop();
}
// 断开连接释放资源
reader.close();
writer.close();
socket.close();
return;
} else {
// 在文本区显示该消息
jTextArea.append(message + "\n");
// 将该消息广播给其他用户
broadcastMsg(message);
} } catch (Exception e) {
// TODO: handle exception
}
}
} // 广播消息
public void broadcastMsg(String msg) {
for (int i = clientList.size() - 1; i >= 0; i--) {
clientList.get(i).getWriter().println(msg);
clientList.get(i).getWriter().flush();
}
}
}
}

1.4 客户端类

客户端类比较简单,只要创建一个线程,与服务器连接即可。

public class Client extends Thread {
// 写该类的成员变量
private Socket socket=null;
private PrintWriter out=null;
private BufferedReader in=null; private JTextArea area; // 写构造函数,完成初始化
public Client(String ip,int nPort,JTextArea area){
try {
socket=new Socket(ip, nPort); } catch (UnknownHostException e) {
System.err.println("初始化失败!");
} catch (IOException e) {
System.err.println("初始化失败!");
}
start(); // 启动线程,让线程开始工作
this.area=area;
this.area.setText("初始化成功,连接上服务器!\n");
} // 发送消息
public void sendMsg(String msg){
if(socket.isConnected()==true){
out.println("客户端:"+msg);
out.flush();// 将消息推送给客户端
}
} public void run(){
while(true){
try {
// 获取客户端的输入流
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
in = new BufferedReader(isr);
// 获取客户端的输出流
out = new PrintWriter(socket.getOutputStream(), true); while(true){
// 不停的读取从服务器发来的信息
String info = in.readLine();
area.append(info + "\n");
}
} catch (Exception e) {
System.err.println("发生数据流读取错误,程序退出!");
System.exit(1);
} finally{
try {
// 结束,扫尾工作
out.close();
in.close();
socket.close();
} catch (Exception e2) {
}
}
}
}
}

1.5 运行结果

运行结果如下所示:

JavaSE简单实现多线程聊天的更多相关文章

  1. Python 简单的多线程聊天

    # client 端 import socket ip_port = ('127.0.0.1', 8091) sk = socket.socket() sk.connect(ip_port) prin ...

  2. 【C++】基于socket的多线程聊天室(控制台版)

    以前学习socket网络编程和多线程编程的时候写的一个练手程序 聊天室基本功能: 1.用户管理:登录,注册,登出,修改用户名,修改密码 2.聊天室功能:群聊,私聊,获取在线用户列表,获取所有用户列表 ...

  3. java Socket多线程聊天程序

    参考JAVA 通过 Socket 实现 TCP 编程 参考java Socket多线程聊天程序(适合初学者) 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包 ...

  4. 使用Servlet和JSP实现一个简单的Web聊天室系统

    1 问题描述                                                利用Java EE相关技术实现一个简单的Web聊天室系统,具体要求如下. (1)编写一个登录 ...

  5. Unity3D 实现简单的语音聊天 [iOS版本]

    现在很多手机游戏中的聊天系统都加入语音聊天的功能,相比于传统的文字聊天,语音聊天在MMORPG中显得尤为重要,毕竟直接口头交流总比你码字快得多了,也更直观些. 实现语音聊天的方法很多,U3D中有不少第 ...

  6. 一个简单的多线程Python爬虫(一)

    一个简单的多线程Python爬虫 最近想要抓取拉勾网的数据,最开始是使用Scrapy的,但是遇到了下面两个问题: 前端页面是用JS模板引擎生成的 接口主要是用POST提交参数的 目前不会处理使用JS模 ...

  7. 【Python网络编程】多线程聊天软件程序

    课程设计的时候制作的多线程聊天软件程序 基于python3.4.3 import socket import pickle import threading import tkinter import ...

  8. CreateThread简单那多线程编程

    CreateThread简单那多线程编程 作者:vpoet mail:vpoet_sir@163.com 在进行多任务处理的时候我们往往会用到多线程技术,多线程理论上是多个线程同事处理不同的工作,但是 ...

  9. 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

    基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...

随机推荐

  1. 什么是MySQL

    数据库(Database)是按照数据结构来组织.存储和管理数据的仓库, 每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据. 我们也可以将数据存储在文件中,但是在文件中读 ...

  2. python基础之map/reduce/filter/sorted

    ---map(fun,iterable) 首先来看一看map()函数,map函数接受两个参数,第一个参数是函数的名称,第二个参数一个可迭代对象.即map(fun,iterable) map函数就是将具 ...

  3. kubernetes 1.9部署实践

    目录 简要说明 环境说明 安装前的约定 配置etcd 生成相关证书 证书类型说明 cfssl配置 证书相关配置 生成ca证书 生成kubernetes证书 生成kubectl证书 生成kube-pro ...

  4. C++持有Object-C对象时容易内存泄露

    在IOS项目中,可以将C++与Object-C混编,不过必须放在实现文件.mm中. 在.mm中,我们可能创建了一个C++对象A,而它持有一个Object-C对象B作为成员变量.当A对象被释放掉的时候, ...

  5. bzoj千题计划140:bzoj4519: [Cqoi2016]不同的最小割

    http://www.lydsy.com/JudgeOnline/problem.php?id=4519 最小割树 #include<queue> #include<cstdio&g ...

  6. [2009国家集训队]小Z的袜子(hose) 浅谈莫队

    浅谈莫队 推荐学习博客 http://foreseeable97.logdown.com/posts/158522-233333 借用题目: bzoj 2038 2009 国家集训队 小Z的袜子htt ...

  7. 什么是EOF -- 转

    转载地址:http://www.ruanyifeng.com/blog/2011/11/eof.html 我学习C语言的时候,遇到的一个问题就是EOF. 它是end of file的缩写,表示&quo ...

  8. python读写mysql

    读取mysql数据 # -*- coding: utf-8 -*- # 导入必要模块 import pandas as pd from sqlalchemy import create_engine ...

  9. Linux基础-编译安装Python

    终于涉及一点儿专业的了,说实话,对于目前的我难度还是挺大的,这句话送给未来的自己 挑战开始: 首先了解一下Python3.6,底层是由c++开发的,所以在linux下需要C++的支持,必然少不了gcc ...

  10. vue-cli环境搭建初探!

    1.先安装nodejs环境 https://npm.taobao.org/mirrors/node (选择版本) 下一步 下一步 默认安装就行 2.检查node和npm的是否成功安装 node -v ...