WebSocket实现Android客户端之间的简单通讯

来源:互联网 发布:算法设计最值 编辑:程序博客网 时间:2024/06/05 02:59

作为一个刚毕业的程序猴,自觉的自己能力比较欠缺,正好最近公司没有什么项目,就考虑自己动手做个项目。想来想去,就把自己一直想做的即时通讯拿出来做做。可是问题来了,作为一个Android开发人员,一直没有接触后台的人来说,做一个完整的项目还是比较麻烦的。于是只有边学边做了。

对于通讯来说,我对Websocket情有独钟,可能是因为在学校的时候听说过吧。话不多说,经过几天的调研,终于在搭了一个简单的Servlet+websocket服务器,并且能在Android客户端上实现不同客户端之间的通讯。我想这是万里长征的第一步的成功吧。话不多说,进入正题。

效果图如下:




Android端这里使用的是Autobahn的包,支持Android 使用Websocket,下载地址:http://autobahn.ws/android/downloads/;

具体与服务器的连接方法WebSocketConnection. Connect()方法,通过WebSocketHandler进行与服务器连接通讯。里面的具体方法不再详述。

与不同的客户端进行通讯的思路为:

Step1:在连接成功的时候,向服务器发送自己的用户名,服务器做用户标记;

Step2: 发送消息,格式为“XX@XXX”,@前面表示将要发送的对象,“all”表示群发,@后面表示发送的消息。

Step3: 当然不能忘了网路权限。

 

 

Android端的只有代码如下:


[java] view plain copy
  1. package henry.websocket.test;  
  2.   
  3. import android.support.v7.app.AppCompatActivity;  
  4. import android.os.Bundle;  
  5. import android.util.Log;  
  6. import android.view.View;  
  7. import android.widget.Button;  
  8. import android.widget.EditText;  
  9. import android.widget.TextView;  
  10. import android.widget.Toast;  
  11.   
  12. import de.tavendo.autobahn.WebSocketConnection;  
  13. import de.tavendo.autobahn.WebSocketException;  
  14. import de.tavendo.autobahn.WebSocketHandler;  
  15.   
  16. public class MainActivity extends AppCompatActivity implements View.OnClickListener {  
  17.   
  18.     private static final String wsurl = "ws://172.16.50.126:8080/websocketServer";  
  19.     private static final String TAG = "MainActivity";  
  20.     private WebSocketConnection mConnect = new WebSocketConnection();  
  21.     private EditText mContent;  
  22.     private Button mSend;  
  23.     private TextView mText;  
  24.     private EditText mUserName;  
  25.     private EditText mToSb;  
  26.   
  27.     @Override  
  28.     protected void onCreate(Bundle savedInstanceState) {  
  29.         super.onCreate(savedInstanceState);  
  30.         setContentView(R.layout.activity_main);  
  31.         bindObject();  
  32.         connect();  
  33.     }  
  34.   
  35.     /** 
  36.      * 绑定控件 
  37.      */  
  38.     private void bindObject() {  
  39.         mContent = (EditText) findViewById(R.id.et_content);  
  40.         mSend = (Button) findViewById(R.id.btn_send);  
  41.         mText = (TextView) findViewById(R.id.tv_test);  
  42.         mUserName = (EditText) findViewById(R.id.et_username);  
  43.         mToSb = (EditText) findViewById(R.id.et_to);  
  44.         mSend.setOnClickListener(this);  
  45.     }  
  46.   
  47.     /** 
  48.      * websocket连接,接收服务器消息 
  49.      */  
  50.     private void connect() {  
  51.         Log.i(TAG, "ws connect....");  
  52.         try {  
  53.             mConnect.connect(wsurl, new WebSocketHandler() {  
  54.                 @Override  
  55.                 public void onOpen() {  
  56.                     Log.i(TAG, "Status:Connect to " + wsurl);  
  57.                     sendUsername();  
  58.                 }  
  59.   
  60.                 @Override  
  61.                 public void onTextMessage(String payload) {  
  62.                     Log.i(TAG, payload);  
  63.                     mText.setText(payload != null ? payload : "");  
  64. //                    mConnect.sendTextMessage("I am android client");  
  65.                 }  
  66.   
  67.                 @Override  
  68.                 public void onClose(int code, String reason) {  
  69.                     Log.i(TAG, "Connection lost..");  
  70.                 }  
  71.             });  
  72.         } catch (WebSocketException e) {  
  73.             e.printStackTrace();  
  74.         }  
  75.     }  
  76.   
  77.     /** 
  78.      * 发送用户名给服务器 
  79.      */  
  80.     private void sendUsername() {  
  81.         String user = mUserName.getText().toString();  
  82.         if (user != null && user.length() != 0)  
  83.             mConnect.sendTextMessage(user);  
  84.         else  
  85.             Toast.makeText(getApplicationContext(), "不能为空", Toast.LENGTH_SHORT).show();  
  86.     }  
  87.   
  88.     /** 
  89.      * 发送消息 
  90.      * 
  91.      * @param msg 
  92.      */  
  93.     private void sendMessage(String msg) {  
  94.         if (mConnect.isConnected()) {  
  95.             mConnect.sendTextMessage(msg);  
  96.         } else {  
  97.             Log.i(TAG, "no connection!!");  
  98.         }  
  99.     }  
  100.   
  101.     @Override  
  102.     protected void onDestroy() {  
  103.         super.onDestroy();  
  104.         mConnect.disconnect();  
  105.     }  
  106.   
  107.     @Override  
  108.     public void onClick(View view) {  
  109.         if (view == mSend) {  
  110.             String content = mToSb.getText().toString() + "@" + mContent.getText().toString();  
  111.             if (content != null && content.length() != 0)  
  112.                 sendMessage(content);  
  113.             else  
  114.                 Toast.makeText(getApplicationContext(), "不能为空", Toast.LENGTH_SHORT).show();  
  115.         }  
  116.     }  
  117. }  

服务器端主要是借鉴别人的,具体见http://www.mobile-open.com/2016/968477.html;主要是对于客户端连接的用户进行处理与标记,并且根据客户端的要求向不同对象转发消息,实现不同客户端之间的通讯。具体代码如下:

[java] view plain copy
  1. import javax.websocket.*;  
  2. import javax.websocket.server.ServerEndpoint;  
  3. import java.io.IOException;  
  4. import java.util.HashMap;  
  5. import java.util.concurrent.CopyOnWriteArraySet;  
  6.   
  7. /** 
  8.  * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端, 
  9.  * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端 
  10.  */  
  11. @ServerEndpoint("/websocketServer")  
  12. public class WebSocketTest {  
  13.   
  14.     //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。  
  15.     private static int onlineCount = 0;  
  16.   
  17.     //connect key为session的ID,value为此对象this  
  18.     private static final HashMap<String, Object> connect = new HashMap<String, Object>();  
  19.     //userMap key为session的ID,value为用户名  
  20.     private static final HashMap<String, String> userMap = new HashMap<String, String>();  
  21.     //与某个客户端的连接会话,需要通过它来给客户端发送数据  
  22.     private Session session;  
  23.     //判断是否是第一次接收的消息  
  24.     private boolean isfirst = true;  
  25.   
  26.     private String username;  
  27.   
  28.     /** 
  29.      * 连接建立成功调用的方法 
  30.      * 
  31.      * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据 
  32.      */  
  33.     @OnOpen  
  34.     public void onOpen(Session session) {  
  35.         this.session = session;  
  36.         connect.put(session.getId(), this);//获取Session,存入Hashmap中  
  37.     }  
  38.   
  39.     /** 
  40.      * 连接关闭调用的方法 
  41.      */  
  42.     @OnClose  
  43.     public void onClose(Session session) {  
  44.   
  45.         String usr = userMap.get(session.getId());  
  46.         userMap.remove(session.getId());  
  47.         connect.remove(session.getId());  
  48.   
  49.   
  50.         System.out.println(usr + "退出!当前在线人数为" + connect.size());  
  51.     }  
  52.   
  53.     /** 
  54.      * 收到客户端消息后调用的方法 
  55.      * 
  56.      * @param message 客户端发送过来的消息 
  57.      * @param session 可选的参数 
  58.      */  
  59.     @OnMessage  
  60.     public void onMessage(String message, Session session) {  
  61.   
  62.         if (isfirst) {  
  63.             this.username = message;  
  64.             System.out.println("用户" + username + "上线,在线人数:" + connect.size());  
  65.             userMap.put(session.getId(), username);  
  66.             isfirst = false;  
  67.         } else {  
  68.             String[] msg = message.split("@"2);//以@为分隔符把字符串分为xxx和xxxxx两部分,msg[0]表示发送至的用户名,all则表示发给所有人  
  69.             if (msg[0].equals("all")) {  
  70.                 sendToAll(msg[1], session);  
  71.             } else {  
  72.                 sendToUser(msg[0], msg[1], session);  
  73.             }  
  74.         }  
  75.     }  
  76.   
  77.     /** 
  78.      * 发生错误时调用 
  79.      * 
  80.      * @param session 
  81.      * @param error 
  82.      */  
  83.     @OnError  
  84.     public void onError(Session session, Throwable error) {  
  85.         System.out.println("发生错误");  
  86.         error.printStackTrace();  
  87.     }  
  88.   
  89.     /** 
  90.      * 给所有人发送消息 
  91.      * 
  92.      * @param msg     发送的消息 
  93.      * @param session 
  94.      */  
  95.     private void sendToAll(String msg, Session session) {  
  96.         String who = "";  
  97.         //群发消息  
  98.         for (String key : connect.keySet()) {  
  99.             WebSocketTest client = (WebSocketTest) connect.get(key);  
  100.             if (key.equalsIgnoreCase(userMap.get(key))) {  
  101.                 who = "自己对大家说 : ";  
  102.             } else {  
  103.                 who = userMap.get(session.getId()) + "对大家说 :";  
  104.             }  
  105.             synchronized (client) {  
  106.                 try {  
  107.                     client.session.getBasicRemote().sendText(who + msg);  
  108.                 } catch (IOException e) {  
  109.                     connect.remove(client);  
  110.                     e.printStackTrace();  
  111.                     try {  
  112.                         client.session.close();  
  113.                     } catch (IOException e1) {  
  114.                         e1.printStackTrace();  
  115.                     }  
  116.                 }  
  117.             }  
  118.   
  119.         }  
  120.     }  
  121.   
  122.     /** 
  123.      * 发送给指定用户 
  124.      * 
  125.      * @param user    用户名 
  126.      * @param msg     发送的消息 
  127.      * @param session 
  128.      */  
  129.     private void sendToUser(String user, String msg, Session session) {  
  130.         boolean you = false;//标记是否找到发送的用户  
  131.         for (String key : userMap.keySet()) {  
  132.             if (user.equalsIgnoreCase(userMap.get(key))) {  
  133.                 WebSocketTest client = (WebSocketTest) connect.get(key);  
  134.                 synchronized (client) {  
  135.                     try {  
  136.                         client.session.getBasicRemote().sendText(userMap.get(session.getId()) + "对你说:" + msg);  
  137.                     } catch (IOException e) {  
  138.                         connect.remove(client);  
  139.                         try {  
  140.                             client.session.close();  
  141.                         } catch (IOException e1) {  
  142.                             e1.printStackTrace();  
  143.                         }  
  144.                     }  
  145.                 }  
  146.                 you = true;//找到指定用户标记为true  
  147.                 break;  
  148.             }  
  149.   
  150.         }  
  151.         //you为true则在自己页面显示自己对xxx说xxxxx,否则显示系统:无此用户  
  152.         if (you) {  
  153.             try {  
  154.                 session.getBasicRemote().sendText("自己对" + user + "说:" + msg);  
  155.             } catch (IOException e) {  
  156.                 e.printStackTrace();  
  157.             }  
  158.         } else {  
  159.             try {  
  160.                 session.getBasicRemote().sendText("系统:无此用户");  
  161.             } catch (IOException e) {  
  162.                 e.printStackTrace();  
  163.             }  
  164.         }  
  165.   
  166.     }  
  167.   
  168. }  

虽然实现了简单的通讯,但要做成一个完整的项目,这还远远不够,下面要考虑的东西还很多。

代码地址:http://download.csdn.net/detail/henry__mark/9637322;