Android开发之聊天室

Posted 骨灵冷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发之聊天室相关的知识,希望对你有一定的参考价值。

本案例目的在于开发一个简单的聊天室功能,所有代码都是本人调试修改之后可以正常使用,主要功能在于通过多线程技术由服务器接收客户端的请求,之后将聊天内容发送给每个接入服务器的每个客户端。另外实现了登录功能,只有登录验证之后才可以实现聊天。具体的技术细节在本栏目不涉及,主要是多线程基于Socket,具体代码如下:

首先是简易的聊天模型图:


客户端代码如下:

功能为指定socket连接的ip地址和端口号,客户端分为2个线程A和B,其中A线程负责登录连接,B线程分为2个子线程,第一个是向服务器发送数据,第2个为从服务器接收数据

public class MainActivity extends Activity 
	/**
	 * IP地址及端口号配置
	 */
	private final static String IP_ADDRESS = "172.21.212.158";
	private final static int PORT = 12345;
	/**
	 * 控件变量
	 */
	private Button btn_sent,btn_loadon;
	private EditText editText,edit_username,edit_password;
	private TextView tv_content;
	private boolean isConn = false;
	private Handler handler = new Handler()
		public void handleMessage(Message msg) 
			if (msg.obj.toString().contains("登录成功")) 
				isConn = true;
			
			showToast(msg.what,msg.obj);
		;
	;
	private ClientThread clientThread;
	private Socket  socket ;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /**
         * 初始化控件
         */
        btn_sent = (Button) findViewById(R.id.btn_sent);
        editText = (EditText) findViewById(R.id.edit_text);
        tv_content = (TextView) findViewById(R.id.tv_content);
        btn_loadon = (Button) findViewById(R.id.btn_loadon);
        edit_password = (EditText) findViewById(R.id.edit_password_input);
        edit_username = (EditText) findViewById(R.id.edit_username_input);
    
        btn_loadon.setOnClickListener(new View.OnClickListener() 
			
			@Override
			public void onClick(View v) 
				//获取登录名和密码
				String user = edit_username.getText().toString().trim();
				String pass = edit_password.getText().toString().trim();
				//启动登录线程
				new Thread(new ConnServer(user, pass)).start();
			
		);
        if (socket == null) 
			Toast.makeText(getApplicationContext(), "Null Socket", Toast.LENGTH_SHORT).show();
		
        //响应发送按钮
        btn_sent.setOnClickListener(new View.OnClickListener() 
			
			@Override
			public void onClick(View v) 
				// TODO Auto-generated method stub
				//将要发送的内容包装给成消息,因为后面要发送给服务器
								
					new NewClientTask(socket).execute(editText.getText().toString().trim()+"\\n");
					editText.setText("");		
			
		);
    
    public void showToast(int flag,Object msgobj)
    
    	switch (flag) 
		case 0:
			Toast.makeText(this, msgobj.toString(), Toast.LENGTH_SHORT).show();
			break;
		case 1:
			Toast.makeText(this, msgobj.toString(), Toast.LENGTH_SHORT).show();
		default:
			break;
		
    
    /**
     * 连接服务器的线程
     */
    private class ConnServer implements Runnable
    	private String username = null;
    	private String password = null;
    	private int waitTime = 0;
    	private boolean hasSendConnMessage = false;
    	public ConnServer(String user,String pass)
    		this.username = user;
    		this.password = pass;
    	
    	//创建构造函数
    	
		@Override
		public void run() 
			// TODO Auto-generated method stub
			try 
				//向服务器传递数据
				socket = new Socket(IP_ADDRESS, PORT);
				OutputStream os = socket.getOutputStream();
				byte[] buffer = new byte[512];
				String str = this.username+"#"+this.password;
				buffer = str.getBytes();
				os.write(buffer);
				hasSendConnMessage = true;

			 catch (UnknownHostException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
			 catch (IOException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			InputStream is = null;
			if (hasSendConnMessage) 	
				try 
					is = socket.getInputStream();
					while (is == null) 
						waitTime += 1000;
					
					byte[] buffer2 = new byte[512];
					is.read(buffer2);
					String reuslt = new String(buffer2,"utf-8");
					//建立一个消息
					Message msg = new Message();
					msg.what = 1;
					msg.obj = reuslt;
					handler.sendMessage(msg);
					buffer2 = null;
				 catch (IOException e) 
					// TODO Auto-generated catch block
					e.printStackTrace();
				
			
			if (waitTime > 5000) 
				Message msg = new Message();
				msg.what = 0;
				msg.obj = "登录超时";
				handler.sendMessage(msg);
			
		
    	
    
    /**
     * 客户端线程类,实现了Runnble接口
     * @author sjm
     *
     */
    private class NewClientTask extends AsyncTask<String, Void, String>
    
    	private Socket clientScoket ;
    	public NewClientTask(Socket s)
    		this.clientScoket = s;
    	
		@Override
		protected void onPostExecute(String result) 
			// TODO Auto-generated method stub
			tv_content.append(result+"\\n");
			
		

		@Override
		protected String doInBackground(String... params) 
			// TODO Auto-generated method stub
		
			//接收自服务器的数据
			String result = null;
			StringBuilder sb = new StringBuilder();
			try 
				//发送给服务器
				OutputStream os = clientScoket.getOutputStream();
				os.write(params[0].getBytes("utf-8"));
				
				InputStream is = clientScoket.getInputStream();
				byte[] buffer = new byte[512];
				is.read(buffer);
				result = new String(buffer, "utf-8");
				
			 catch (UnknownHostException e) 
				// TODO Auto-generated catch block
				try 
					clientScoket.close();
				 catch (IOException e1) 
					// TODO Auto-generated catch block
					e1.printStackTrace();
				
				e.printStackTrace();
			 catch (IOException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			return result;
		
    	
    


接下来是服务器的功能:主要是用于验证登录,以及分发收到的聊天消息,需要注意的是用户名与密码发送给服务器的格式需要自定义,这里我用的是USER#PASS:





import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.ObjectInputStream.GetField;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.logging.Handler;


/**
 * 我的服务器:用来接收客户端的消息,并且把消息再发送给每个客户端
 * @author sjm
 *
 */
public class MyServer
//建立socketArray对象存储各个socket
public static ArrayList<Socket> socketArray = new ArrayList<Socket>();
private final static int PORT = 12345;
private static String USERNAME = "SJM";
private static String PASSWORDS = "1234";
private static Socket clientsocket = null;
private static boolean hasClient = false;
public static void main(String[] args)
// TODO Auto-generated method stub
//1、启动服务端
try
ServerSocket server = new ServerSocket(PORT);
while(true)

System.out.println("等待一个连接:");
clientsocket = server.accept();
System.out.println("接收到一个连接请求");
//将该客户端socket放置到socketArray当中
//设置登录权限,当用户名与密码均满足条件时才开辟线程
//实现方法是开辟一个验证登录的线程,满足则继续
new Thread(new clientPermission(clientsocket)).start();

catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();




/**
* 验证登录的线程
* @author sjm
*
*/
private static class clientPermission implements Runnable
private Socket socket;
private String userName = null;//用户名
private String passWords = null;//密码
public clientPermission(Socket s)
this.socket = s;

@Override
public void run()
// TODO Auto-generated method stub
try
InputStream is = this.socket.getInputStream();
byte[] buffer = new byte[512];
is.read(buffer);

String br = new String(buffer);
System.out.println(br);
//用户名和密码的数据格式是在传入过来的时候自定义的,如username#passwords
if (br != null)
String str = br.toString();
String[] str2 = str.split("#");
userName = str2[0];
passWords = str2[1];
System.out.println(str);
hasClient = userName.contains(USERNAME)&&passWords.contains(PASSWORDS)?true:false;
System.out.println(String.valueOf(hasClient));
if (hasClient)
//若验证正确
socketArray.add(socket);
//返回一个消息
OutputStream os = socket.getOutputStream();
os.write(new String("登录成功").getBytes("utf-8"));
buffer = null;
//为该客户端开辟一个线程
new Thread(new serverThread(socket)).start();

else
OutputStream os = socket.getOutputStream();
os.write(new String("密码错误").getBytes("utf-8"));
is.close();
socket.close();
socketArray.remove(socket);
    return ;


else

OutputStream os = socket.getOutputStream();
os.write(new String("密码不能为空").getBytes("utf-8"));
is.close();
socket.close();
socketArray.remove(socket);
return;

catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();




private static class serverThread implements Runnable
private Socket client_Socket;
/**
* 线程构造函数
* @param s
*/
public serverThread(Socket s)
this.client_Socket = s;

@Override
public void run()
// TODO Auto-generated method stub
try
System.out.println("启动啦");
while(true)

String str = null;
byte[] buffer = new byte[512];
InputStream is = client_Socket.getInputStream();
if (is != null)
is.read(buffer);
//str = new String(buffer, "utf-8");
//System.out.println("接收到消息"+str);
for(Socket s : socketArray)

OutputStream os = s.getOutputStream();
os.write(buffer);



catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
if (client_Socket != null)
socketArray.remove(client_Socket);





以上是关于Android开发之聊天室的主要内容,如果未能解决你的问题,请参考以下文章

Android之聊天室设计与开发

Android开发学习之路--UI之简单聊天界面

android 如何开发 视频聊天

iOS开发开辟线程总结--NSThread

视频零基础学Android开发:蓝牙聊天室APP

android 聊天气泡第三方UI之BubbleView使用