android客户端容易的聊天程序实现

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手机客户端上的简单的聊天功能了,运行结果给如下:

android客户端容易的聊天程序实现android客户端容易的聊天程序实现android客户端容易的聊天程序实现android客户端容易的聊天程序实现