android客户端简单的聊天程序实现
android聊天室的聊天功能吧,先说说服务器端的代码及其功能吧
server.java : 负责服务器的界面,以及更服务器主线程ServerThread的启动,产生了BroadCast广播,产生ClientThread线程
ServerThread.java:服务器监听的端口线程,负责创建ServerSocket及监听是否有新的客户端连接,并记录客户端连接及需要发送的信息,产生了BroadCast广播
BroadCast.java: 服务器向客户端广播线程,负责向客户端发送消息,产生ClientThread线程
ClientThread.java:维持服务器和单个客户端的连接线程,负责接受客户端发来是信息
好了接着就看看他们的代码吧!!
1.server.java-------创建ServerThread对象启动run方法
package com.wang;
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.event.*;
public class Server extends JFrame implements ActionListener {
/****
*服务器端主程序负责界面,以及服务段主线程ServerThread的启动
* 服务端主线程ServerThread又产生BroadCast及ClientThread线程 建立服务器端主界面中所用到的布局方式
***/
// 边框容器
BorderLayout borderLayout1 = new BorderLayout();
BorderLayout borderLayout2 = new BorderLayout();
// 创建面板
JPanel jPanel1 = new JPanel();
JPanel jPanel2 = new JPanel();
// 创建按钮
JButton jButton1 = new JButton();
JButton jButton2 = new JButton();
JScrollPane jScrollPane1 = new JScrollPane();
// 创建服务器端接收信息文本框
static JTextArea jTextArea1 = new JTextArea();
boolean bool = false, start = false;
// 声明ServerThread线程类对象
ServerThread serverThread;
Thread thread;
// 构造函数,用于初始化
public Server() {
super("Server");
// 设置内容面板布局方式
getContentPane().setLayout(borderLayout1);
// 初始化按钮组件
jButton1.setText("启动服务器");
// 按钮的动作设置监听事件
jButton1.addActionListener(this);
jButton2.setText("关闭服务器");
// 按钮的动作设置监听事件
jButton2.addActionListener(this);
// 初始化jPanel1面板对象,并向其中加入组件,上北
this.getContentPane().add(jPanel1, java.awt.BorderLayout.NORTH);
jPanel1.add(jButton1);
jPanel1.add(jButton2);
// 初始化jPanel2面板对象,并向其中加入组件,
jTextArea1.setText("");
jPanel2.setLayout(borderLayout2);
jPanel2.add(jScrollPane1, java.awt.BorderLayout.CENTER);
jScrollPane1.getViewport().add(jTextArea1);
this.getContentPane().add(jPanel2, java.awt.BorderLayout.CENTER);
this.setSize(400, 400);
this.setVisible(true);
}
public static void main(String[] args) {
Server sever = new Server();
sever.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
// 服务器界面中按钮事件处理
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jButton1) {
// 声明一个ServerThread对象
serverThread = new ServerThread();
serverThread.start();
} else if (e.getSource() == jButton2) {
bool = false;
start = false;
serverThread.finalize();
this.setVisible(false);
}
}
}
2.ServerThread.java -----创建Broadcast对象,启动该线程,实现run方法后,不断的向客户端发送消息,ServerThread开启后,不断的获取新的客户端并监听是否发送消息
package com.wang;
import java.util.*;
import java.io.*;
import java.net.*;
public class ServerThread extends Thread
// 服务器监听端口线程
{
// 声明ServerSocket类对象
ServerSocket serverSocket;
// 指定服务器监听端口常量
public static final int PORT = 80;
/**
* 创建一个Vector对象,用于存储客户端连接的ClientThread对象 , ClientThread类维持服务器与单个客户端的连接线程
* 负责接收客户端发来的信息,clients负责存储所有与服务器建立连接的客户端
**/
Vector<ClientThread> clients;
// 创建一个Vector对象,用于存储客户端发送过来的信息
Vector<Object> messages;
// BroadCast类负责服务器向客户端广播消息
BroadCast broadcast;
String ip;
InetAddress myIPaddress = null;
public ServerThread() {
/***
* 创建两个Vector数组非常重要 , clients负责存储所有与服务器建立连接的客户端,
* messages负责存储服务器接收到的未发送出去的全部客户端的信息
*
**/
clients = new Vector<ClientThread>();
messages = new Vector<Object>();
try {
// 创建ServerSocket类对象
serverSocket = new ServerSocket(PORT);
} catch (IOException E) {
}
// 获取本地服务器地址信息
try {
myIPaddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
System.out.println(e.toString());
}
ip = myIPaddress.getHostAddress();
Server.jTextArea1.append("服务器地址:" + ip + "端口号:"
+ String.valueOf(serverSocket.getLocalPort()) + "\n");
// 创建广播信息线程并启动
broadcast = new BroadCast(this);
broadcast.start();
}
/**
* 注意:一旦监听到有新的客户端创建即new Socket(ip, PORT)被执行,
* 就创建一个ClientThread来维持服务器与这个客户端的连接
**/
public void run() {
while (true) {
try {
// 获取客户端连接,并返回一个新的Socket对象
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress().getHostAddress());
// 创建ClientThread线程并启动,可以监听该连接对应的客户端是否发送来消息, 并获取消息
ClientThread clientThread = new ClientThread(socket, this);
clientThread.start();
if (socket != null) {
synchronized (clients) {
// 将客户端连接加入到Vector数组中保存
clients.addElement(clientThread);
}
}
} catch (IOException E) {
System.out.println("发生异常:" + E);
System.out.println("建立客户端联机失败!");
System.exit(2);
}
}
}
public void finalize() {
try {
// 关闭serverSocket方法
serverSocket.close();
} catch (IOException E) {
}
serverSocket = null;
}
}
3.BroadCast.java------
package com.wang;
import java.io.*;
public class BroadCast extends Thread { // 服务器向客户端广播线程
ClientThread clientThread;
// 声明ServerThread对象
ServerThread serverThread;
String str;
public BroadCast(ServerThread serverThread) {
this.serverThread = serverThread;
}
// 该方法的作用是不停地向所有客户端发送新消息
public void run() {
while (true) {
try {
// 线程休眠200 ms
Thread.sleep(200);
} catch (InterruptedException E) {
}
// 同步化serverThread.messages
synchronized (serverThread.messages) {
// 判断是否有未发的消息
if (serverThread.messages.isEmpty()) {
continue;
}
// 获取服务器端存储的需要发送的第一条数据信息
str = (String) this.serverThread.messages.firstElement();
}
// 同步化serverThread.clients
synchronized (serverThread.clients) {
// 利用循环获取服务器中存储的所有建立的与客户端的连接
for (int i = 0; i < serverThread.clients.size(); i++) {
clientThread = (ClientThread) serverThread.clients
.elementAt(i);
try {
// 向记录的每一个客户端发送数据信息
clientThread.out.writeUTF(str);
} catch (IOException E) {
}
}
// 从Vector数组中删除已经发送过的那条数据信息
this.serverThread.messages.removeElement(str);
}
}
}
}
4.ClientThread.java----获得Socket的输入输出流,向客户端接收或者发送数据
package com.wang;
import java.net.*;
import java.io.*;
public class ClientThread extends Thread
{
/**
* 维持服务器与单个客户端的连接线程,负责接收客户端发来的信息,
* 声明一个新的Socket对象,
* 用于保存服务器端用accept方法得到的客户端的连接
**/
Socket clientSocket;
//声明服务器端中存储的Socket对象的数据输入/输出流
DataInputStream in = null;
DataOutputStream out = null;
//声明ServerThread对象
ServerThread serverThread;
public ClientThread(Socket socket,ServerThread serverThread)
{
clientSocket=socket;
this.serverThread=serverThread;
try
{
//创建服务器端数据输入/输出流
in = new DataInputStream(clientSocket.getInputStream());
out = new DataOutputStream(clientSocket.getOutputStream());
}
catch (IOException e2)
{
System.out.println("发生异常"+e2);
System.out.println("建立I/O通道失败!");
System.exit(3);
}
}
//该方法监听该连接对应得客户端是否有消息发送
public void run()
{
while(true)
{
try
{
//读入客户端发送来的信息
String message=in.readUTF();
synchronized(serverThread.messages)
{
if(message!=null)
{
//将客户端发送来得信息存于serverThread的messages数组中
serverThread.messages.addElement(message);
//在服务器端的文本框中显示新消息
Server.jTextArea1.append(message+'\n');
}
}
}
catch(IOException E){break;}
}
}
}
5.接着看看手机客户端的布局main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/username"
android:layout_width="270dp"
android:layout_height="wrap_content"
android:hint="请输入用户名:"
android:maxLength="10" >
</EditText>
<Button
android:id="@+id/LoginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登陆" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/ip"
android:layout_width="270dp"
android:layout_height="wrap_content"
android:digits=".1234567890"
android:hint="10.254.1.62" >
</EditText>
<Button
android:id="@+id/LeaveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="退出" />
</LinearLayout>
<EditText
android:id="@+id/history"
android:layout_width="fill_parent"
android:layout_height="280dp" >
</EditText>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/message"
android:layout_width="270dp"
android:layout_height="wrap_content" >
</EditText>
<Button
android:id="@+id/SendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送" />
</LinearLayout>
</LinearLayout>
6.接着看看手机客户端的实现ChatClientActivity.java
package com.wang;
import android.app.Activity;
import android.os.Bundle;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.sql.Date;
import java.text.SimpleDateFormat;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class ChatClientActivity extends Activity implements Runnable {
private EditText usernameEdit;
private EditText ipEdit;
private EditText historyEdit;
private EditText messageEdit;
private Button loginButton;
private Button sendButton;
private Button leaveButton;
/**
* 声明字符串,name存储用户名 chat_txt存储发送信息 chat_in存储从服务器接收到的信息
****/
private String username, ip, chat_txt, chat_in;
// 创建Socket通信端口号常量
public static final int PORT = 80;
// 声明套接字对象
Socket socket;
// 声明线程对象
Thread thread;
// 声明客户器端数据输入输出流
DataInputStream dataInputStream;
DataOutputStream dataOutputStream;
// 是否登录的标记
boolean flag = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 取消标题栏
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
// 设置全屏
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
// 实例化组件
usernameEdit = (EditText) findViewById(R.id.username);
ipEdit = (EditText) findViewById(R.id.ip);
historyEdit = (EditText) findViewById(R.id.history);
messageEdit = (EditText) findViewById(R.id.message);
loginButton = (Button) findViewById(R.id.LoginButton);
sendButton = (Button) findViewById(R.id.SendButton);
leaveButton = (Button) findViewById(R.id.LeaveButton);
// 为三个按钮注册监听器
loginButton.setOnClickListener(listener);
sendButton.setOnClickListener(listener);
leaveButton.setOnClickListener(listener);
}
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
// "进入聊天室"按钮的处理
case R.id.LoginButton:
if (flag == true) {
Toast.makeText(ChatClientActivity.this, "亲,你已经登陆过啦!!!",
Toast.LENGTH_LONG).show();
return;
}
// 获取用户名
username = usernameEdit.getText().toString();
// 获取服务器ip
ip = ipEdit.getText().toString();
// 判断用户名是否有效及ip是否为空
if (username != "" && username != null && username != "用户名输入"
&& ip != null) {
try {
// 创建Socket对象
socket = new Socket(ip, PORT);
// 创建客户端数据输入/输出流,用于对服务器端发送或接收数据
dataInputStream = new DataInputStream(socket
.getInputStream());
dataOutputStream = new DataOutputStream(socket
.getOutputStream());
// 得到系统的时间
Date now = new Date(System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat(
"hh:mm:ss");
String nowStr = format.format(now);
// 输出某某上线啦
dataOutputStream.writeUTF("└(^o^)┘: " + username
+ " : " + nowStr + " 上线啦!");
} catch (IOException e1) {
System.out.println("抱歉连接不成功!!!");
}
thread = new Thread(ChatClientActivity.this);
// 开启线程,监听服务器段是否有消息
thread.start();
// 说明已经登录成功
flag = true;
}
break;
// "发送"按钮的处理
case R.id.SendButton:
if (flag == false) {
Toast.makeText(ChatClientActivity.this, "亲,你还没登录,请先登录!",
Toast.LENGTH_LONG).show();
return;
}
// 获取客户端输入的发言内容
chat_txt = messageEdit.getText().toString();
if (chat_txt != null) {
// 得到当前时间
Date now = new Date(System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
String nowStr = format.format(now);
// 发言,向服务器发送发言的信息
try {
dataOutputStream.writeUTF("(^_^)∠※--->" + username
+ " : " + nowStr + " 说\n" + chat_txt);
} catch (IOException e2) {
}
} else {
try {
dataOutputStream.writeUTF("请发言!!!");
} catch (IOException e3) {
}
}
break;
// "退出聊天室"按钮事件的处理
case R.id.LeaveButton:
if (flag == true) {
if (flag == false) {
Toast.makeText(ChatClientActivity.this,
"亲,你还没登录,请先登录!", Toast.LENGTH_LONG).show();
return;
}
try {
dataOutputStream.writeUTF("(^_^)/~~ " + username
+ "下线了!");
// 关闭socket
dataOutputStream.close();
dataInputStream.close();
socket.close();
} catch (IOException e4) {
}
}
flag = false;
Toast.makeText(ChatClientActivity.this, "已退出!",
Toast.LENGTH_LONG).show();
break;
}
}
};
// 客户端线程启动后的动作
@Override
public void run() {
// 循环执行,作用是一直监听服务器端是否有消息
while (true) {
try {
// 读取服务器发送来的数据信息
chat_in = dataInputStream.readUTF();
chat_in = chat_in + "\n";
// 发送一个消息,要求刷新界面
mHandler.sendMessage(mHandler.obtainMessage());
} catch (IOException e) {
}
}
}
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// 将消息并显示在客户端的对话窗口中
historyEdit.append(chat_in);
// 刷新
super.handleMessage(msg);
}
};
}
7,亲,别忘了由于需要网络,需要添加联网的权限哦!!
<uses-permission android:name="android.permission.INTERNET"/>
8.如果你完成以上功能,就可以实现android手机客户端上的简单的聊天功能了,运行结果给如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!