服务器有三个联网类组成:
服务线程,启动服务器时启动,开启服务ServerSocket用以监听端口收到的客户端信息。
package com.rxz.web;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class ServerThread implements Runnable {
// ServerSocket 类对象
private ServerSocket serverSocket = null;
// 服务器端口设置
private static final int PORT = 8888;
// 存储接收到客户端的消息
public List messagelt = null;
// 服务器启动标志位
private boolean isStart = true;
// 存储客户端socket
public HashMap clientsMap = null;
/**
* 构造函数
*/
public ServerThread(){
// 初始化 message 存储list
messagelt = new ArrayList();
// 初始化客户端socket
clientsMap = new HashMap();
try {
// 开启服务器socket
serverSocket = new ServerSocket(PORT);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 启动消息处理线程
new Thread(new MessageHandlerThread(this)).start();
}
/**
* 一旦有客户端连入,就启动客户端处理线程,并加入服务器客户端HashMap中
*/
@Override
public void run() {
// TODO Auto-generated method stub
while(isStart){
try {
// 获取一个连接客户端
Socket socket = serverSocket.accept();
System.out.println("连入一个客户端:" + socket.getInetAddress().getHostAddress());
// 创建clientHandler线程
ClientHandlerThread clientRunnable = new ClientHandlerThread(socket, this);
Thread clientThread = new Thread(clientRunnable);
clientThread.start();
// 将成功获取到的客户端保存起来
if(socket != null){
synchronized(clientsMap){
clientsMap.put(socket.getInetAddress().getHostAddress(), clientRunnable);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 关闭 ServerSocket
*/
public void finalize(){
// 关闭 ServerSocket
try {
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
serverSocket = null;
}
}
class Message{
public String IP;
public String info;
public Message(String iP, String info) {
super();
IP = iP;
this.info = info;
}
}
客户端处理线程,当有一个客户端连入服务器时,启动一个线程接收客户端信息。
package com.rxz.web;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class ClientHandlerThread implements Runnable {
// 客户端socket
public Socket clientSocket = null;
// socket 的输入,输出流
private DataInputStream in = null;
public DataOutputStream out = null;
// 服务器线程ServerThread
private ServerThread serverThread = null;
/**
* 构造函数
* @param socket
* @param serverThread
*/
public ClientHandlerThread(Socket socket, ServerThread serverThread){
this.serverThread = serverThread;
this.clientSocket = socket;
// 获取对服务器操作的输入输出流
try {
in = new DataInputStream(clientSocket.getInputStream());
out = new DataOutputStream(clientSocket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 监听对应的客户端是否有消息发送
*/
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
String iP = in.readUTF();
String info = in.readUTF();
Message message = new Message(iP, info);
// 打印接收信息
System.out.println("[" + clientSocket.getInetAddress().getHostAddress() + "] -> [" + message.IP + "]:" + message.info);
// 将客户端发送的信息存储到 Message LIST中
synchronized (serverThread.messagelt) {
serverThread.messagelt.add(message);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
}
}
}
package com.rxz.web;
import java.io.IOException;
import java.util.Iterator;
public class MessageHandlerThread implements Runnable{
// 客户端线程
private ClientHandlerThread clientThread = null;
// 服务器线程
private ServerThread serverThread = null;
// 处理的消息
private Message message = null;
/**
* 构造函数
* @param serverThread
*/
public MessageHandlerThread(ServerThread serverThread){
this.serverThread = serverThread;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
// 线程休眠100ms
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取要处理的信息
synchronized (serverThread.messagelt) {
// 判断是否有未发送的信息
if(serverThread.messagelt.isEmpty()){
continue;
}
message = serverThread.messagelt.get(0);
}
// 处理信息
synchronized (serverThread.clientsMap) {
if(message.IP.equals("10.0.2.2")) message.IP = "127.0.0.1";
// 获取要发送的客户端
clientThread = serverThread.clientsMap.get(message.IP);
try {
// 获取所有用户
if(message.info.equals("{GETALL}")){
String info = "";
Iterator iterator = serverThread.clientsMap.keySet().iterator();
while(iterator.hasNext()) {
info += serverThread.clientsMap.get(iterator.next()).clientSocket.getInetAddress().getHostAddress();
info += ";";
}
message.info = info;
}
// 发送数据
clientThread.out.writeUTF(message.info);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 从server LIST中删除掉已经处理的消息
serverThread.messagelt.remove(message);
}
}
}
}
该设计按照单例模式设计,即全局不构造对象,类变量只有一个实例。
这样的设计提高了socket利用率,节省了代码量。
package com.rxz.web;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class ChatClient {
private static Socket clientSocket = null;
private static DataInputStream in = null;
private static DataOutputStream out = null;
private static String IP = "10.0.2.2";
private static int PORT = 8888;
public static Socket getSocket(){
if(clientSocket == null){
try {
clientSocket = new Socket(IP, PORT);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return clientSocket;
}
public static DataInputStream getDataInputStream(){
if(in == null){
getSocket();
try {
in = new DataInputStream(clientSocket.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return in;
}
public static DataOutputStream getDataOutputStream(){
if(out == null){
getSocket();
try {
out = new DataOutputStream(clientSocket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return out;
}
}
Spinner控件的设计,用于选择在线用户;
TextView控件为了显示聊天记录信息;
EditView控件为了获取用户输入的数据信息;
Button控件为了发送用户信息;
package com.rxzchatdemo;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import com.rxz.web.ChatClient;
public class MainActivity extends Activity implements OnClickListener, Runnable {
private Spinner userList = null;
private Button sendBtn = null;
private EditText infoEdit = null;
private String message = null;
private String[] mUsers = null;
private TextView infoArea = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userList = (Spinner) super.findViewById(R.id.userList);
infoEdit = (EditText) findViewById(R.id.infoEdit);
sendBtn = (Button) findViewById(R.id.sendBtn);
infoArea = (TextView) findViewById(R.id.chatArea);
sendBtn.setOnClickListener(this);
try {
//获得本机IP
InetAddress addr = InetAddress.getLocalHost();
String ip = addr.getHostAddress().toString();
ChatClient.getDataOutputStream().writeUTF(ip);
ChatClient.getDataOutputStream().writeUTF("{GETALL}");
String response = ChatClient.getDataInputStream().readUTF();
mUsers = response.split(";");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_spinner_item, mUsers);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
userList.setAdapter(adapter);
new Thread(this).start();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.sendBtn:
String ip = userList.getSelectedItem().toString();
String info = infoEdit.getText().toString();
try {
ChatClient.getDataOutputStream().writeUTF(ip);
ChatClient.getDataOutputStream().writeUTF(info);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
message = ChatClient.getDataInputStream().readUTF();
message += "\n";
mHandler.sendMessage(mHandler.obtainMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private Handler mHandler = new Handler(){
/**
* 获取一个消息,刷新对话框
*/
public void handleMessage(Message msg){
infoArea.append(message);
super.handleMessage(msg);
}
};
}
先启动服务器,然后启动客户端,选择聊天对象,即可实现点对点通信。
PS.几点遗憾:
1.Spinner控件没有获取到点击事件,导致无法更新在线人信息,但这个与socket通信无关,后期将会把更新版本补充上。
2.由于开启虚拟机测试,不能实现现场点对点通信(虚拟机IP相同导致的),真机测试,效果会更好。
3.之前想用传统的ObjectInputStream,然后将信息交换定义为一系列的Message类,但考虑到服务器不知用于java通信,如果这样写可能会让跨语言通信失败。
本实例精华重在一个HashMap映射思想和,MessageHandlerThread消息处理线程的设计,用以实现的点点通信机制。
附上源码:
【源码】 :http://download.csdn.net/detail/hit_rxz/7952127
没有积分的请留言邮箱~