您的位置:首页 > 移动开发 > Android开发

android 客户端简单的聊天程序实现

2015-08-12 09:20 441 查看
android聊天室的聊天功能吧,先说说服务器端的代码及其功能吧

server.java : 负责服务器的界面,以及更服务器主线程ServerThread的启动,产生了BroadCast广播,产生ClientThread线程

ServerThread.java:服务器监听的端口线程,负责创建ServerSocket及监听是否有新的客户端连接,并记录客户端连接及需要发送的信息,产生了BroadCast广播

BroadCast.java: 服务器向客户端广播线程,负责向客户端发送消息,产生ClientThread线程

ClientThread.java:维持服务器和单个客户端的连接线程,负责接受客户端发来是信息

好了接着就看看他们的代码吧!!

1.server.java-------创建ServerThread对象启动run方法

[java] view
plaincopy

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开启后,不断的获取新的客户端并监听是否发送消息

[java] view
plaincopy

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------

[java] view
plaincopy

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的输入输出流,向客户端接收或者发送数据

[java] view
plaincopy

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

[html] view
plaincopy

<?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

[java] view
plaincopy

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,亲,别忘了由于需要网络,需要添加联网的权限哦!!

[html] view
plaincopy

<uses-permission android:name="android.permission.INTERNET"/>

8.如果你完成以上功能,就可以实现android手机客户端上的简单的聊天功能了,运行结果给如下:







内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: