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;}
- }
- }
- }
- <?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>
- 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"/>