打造自己的Android聊天软件(socket篇)

本文介绍如何使用sockets技术构建群聊应用程序,涵盖服务器端、Web端及Android端开发过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实打造属于自己的聊天软件很简单,今天我们讲学习如何简单的写一个群组聊天app,通过sockets实现。这不是唯一的方法,但却是最快和最简单的。最好和最有效的方式应该是使用推送通知(push notifications )而不是sockets。

翻译自http://www.androidhive.info/2014/10/android-building-group-chat-app-using-sockets-part-1/

github 项目地址

github

译者注:原文只给了代码很少有说明,译者会根据自己的情况适当加一些说明,比较复杂的章节将会单独写博客来说明:比如说websockets。

*译者注:重要说明:为防止各种不确定的因素请使用以下开发环境:
- Tomcat7
- javaee-api7*

在这篇文章中我们会做三个东西。第一个也是最重要的就是sockett server。socket server扮演着重要的作用:处理socket客户端连接,传递数据。第二个东西就是web app,你可以通过浏览器加入聊天。最后就是android app。最大的有点就是你可以在web – web, web – android 或者 android – android 进行聊天。

这个系列的文章有点长,把它分为两部分。第一部分就是web篇,第二部分安卓篇。

最终我们的app是这个样子的:

这里写图片描述

究竟App是如何在sockets上工作的?

首先你可以看维基百科或者百度百科。

  1. 首先,我们有一个正在运行的socket server,当安卓app或者web app连接的时候,server打开了一个在服务端与客户端的TCP连接,党有多个用户的时候,serever有能力处理多个连接。
  2. 当socket连接的时候会出发一系列的回调函数,例如 onOpen, onMessage, onExit,在客户端和服务端都会触发。
  3. 服务端与客户端用json进行交互。每一个json都有一个叫flag的字段来确认json的目的。下面的例子是当一个客户加入到对甲中所发送的json
{
    "message": " joined conversation!",
    "flag": "new",
    "sessionId": "4",
    "name": "Ravi Tamada",
    "onlineCount": 6
}
  1. 当收到json信息的时候客户端就会解析。

开始服务端

首先下载三个jar包,放到WEB-INF lib下
google-collection

javeee-api7

json-org

创建一个叫JSONUtils的工具类,包含了众多创建json字符串的方法, 这额json在通信的时候被使用。
下面的代码,你看到每一个json都有一个flag节点,这个节点告诉客户端你的json字符串的目的,根据不同的目的采取不同的行动。

大体上flag有四种

  • self:这个json里包含了session信息,指定了一个特定的客户。当建立起sockets连接的时候,这回事客户端收到的第一个json。
  • new: 这个json在一个新客户加入socket server的时候向所有人广播。
  • message:这个包含了客户端所发送的消息,也会广播给每个人。
  • exit:当客户端退出连接的时候
package info.androidhive.webmobilegroupchat;

import org.json.JSONException;
import org.json.JSONObject;

public class JSONUtils {

    // flags to identify the kind of json response on client side
    private static final String FLAG_SELF = "self", FLAG_NEW = "new",
            FLAG_MESSAGE = "message", FLAG_EXIT = "exit";

    public JSONUtils() {
    }

    /**
     * Json when client needs it's own session details
     * */
    public String getClientDetailsJson(String sessionId, String message) {
        String json = null;

        try {
            JSONObject jObj = new JSONObject();
            jObj.put("flag", FLAG_SELF);
            jObj.put("sessionId", sessionId);
            jObj.put("message", message);

            json = jObj.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return json;
    }

    /**
     * Json to notify all the clients about new person joined
     * */
    public String getNewClientJson(String sessionId, String name,
            String message, int onlineCount) {
        String json = null;

        try {
            JSONObject jObj = new JSONObject();
            jObj.put("flag", FLAG_NEW);
            jObj.put("name", name);
            jObj.put("sessionId", sessionId);
            jObj.put("message", message);
            jObj.put("onlineCount", onlineCount);

            json = jObj.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return json;
    }

    /**
     * Json when the client exits the socket connection
     * */
    public String getClientExitJson(String sessionId, String name,
            String message, int onlineCount) {
        String json = null;

        try {
            JSONObject jObj = new JSONObject();
            jObj.put("flag", FLAG_EXIT);
            jObj.put("name", name);
            jObj.put("sessionId", sessionId);
            jObj.put("message", message);
            jObj.put("onlineCount", onlineCount);

            json = jObj.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return json;
    }

    /**
     * JSON when message needs to be sent to all the clients
     * */
    public String getSendAllMessageJson(String sessionId, String fromName,
            String message) {
        String json = null;

        try {
            JSONObject jObj = new JSONObject();
            jObj.put("flag", FLAG_MESSAGE);
            jObj.put("sessionId", sessionId);
            jObj.put("name", fromName);
            jObj.put("message", message);

            json = jObj.toString();

        } catch (JSONException e) {
            e.printStackTrace();
        }

        return json;
    }
}

创建SocketServer类

package info.androidhive.webmobilegroupchat;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.json.JSONException;
import org.json.JSONObject;

import com.google.common.collect.Maps;

@ServerEndpoint("/chat")
public class SocketServer {

    // set to store all the live sessions
    private static final Set<Session> sessions = Collections
            .synchronizedSet(new HashSet<Session>());

    // Mapping between session and person name
    private static final HashMap<String, String> nameSessionPair = new HashMap<String, String>();

    private JSONUtils jsonUtils = new JSONUtils();

    // Getting query params
    public static Map<String, String> getQueryMap(String query) {
        Map<String, String> map = Maps.newHashMap();
        if (query != null) {
            String[] params = query.split("&");
            for (String param : params) {
                String[] nameval = param.split("=");
                map.put(nameval[0], nameval[1]);
            }
        }
        return map;
    }

    /**
     * Called when a socket connection opened
     * */
    @OnOpen
    public void onOpen(Session session) {

        System.out.println(session.getId() + " has opened a connection");

        Map<String, String> queryParams = getQueryMap(session.getQueryString());

        String name = "";

        if (queryParams.containsKey("name")) {

            // Getting client name via query param
            name = queryParams.get("name");
            try {
                name = URLDecoder.decode(name, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            // Mapping client name and session id
            nameSessionPair.put(session.getId(), name);
        }

        // Adding session to session list
        sessions.add(session);

        try {
            // Sending session id to the client that just connected
            session.getBasicRemote().sendText(
                    jsonUtils.getClientDetailsJson(session.getId(),
                            "Your session details"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Notifying all the clients about new person joined
        sendMessageToAll(session.getId(), name, " joined conversation!", true,
                false);

    }

    /**
     * method called when new message received from any client
     * 
     * @param message
     *            JSON message from client
     * */
    @OnMessage
    public void onMessage(String message, Session session) {

        System.out.println("Message from " + session.getId() + ": " + message);

        String msg = null;

        // Parsing the json and getting message
        try {
            JSONObject jObj = new JSONObject(message);
            msg = jObj.getString("message");
        } catch (JSONException e) {
            e.printStackTrace();
        }

        // Sending the message to all clients
        sendMessageToAll(session.getId(), nameSessionPair.get(session.getId()),
                msg, false, false);
    }

    /**
     * Method called when a connection is closed
     * */
    @OnClose
    public void onClose(Session session) {

        System.out.println("Session " + session.getId() + " has ended");

        // Getting the client name that exited
        String name = nameSessionPair.get(session.getId());

        // removing the session from sessions list
        sessions.remove(session);

        // Notifying all the clients about person exit
        sendMessageToAll(session.getId(), name, " left conversation!", false,
                true);

    }

    /**
     * Method to send message to all clients
     * 
     * @param sessionId
     * @param message
     *            message to be sent to clients
     * @param isNewClient
     *            flag to identify that message is about new person joined
     * @param isExit
     *            flag to identify that a person left the conversation
     * */
    private void sendMessageToAll(String sessionId, String name,
            String message, boolean isNewClient, boolean isExit) {

        // Looping through all the sessions and sending the message individually
        for (Session s : sessions) {
            String json = null;

            // Checking if the message is about new client joined
            if (isNewClient) {
                json = jsonUtils.getNewClientJson(sessionId, name, message,
                        sessions.size());

            } else if (isExit) {
                // Checking if the person left the conversation
                json = jsonUtils.getClientExitJson(sessionId, name, message,
                        sessions.size());
            } else {
                // Normal chat conversation message
                json = jsonUtils
                        .getSendAllMessageJson(sessionId, name, message);
            }

            try {
                System.out.println("Sending Message To: " + sessionId + ", "
                        + json);

                s.getBasicRemote().sendText(json);
            } catch (IOException e) {
                System.out.println("error in sending. " + s.getId() + ", "
                        + e.getMessage());
                e.printStackTrace();
            }
        }
    }

创建web app(Html Css Js)

style.css

body {
    padding: 0;
    margin: 0;
}

.body_container {
    width: 1000px;
    margin: 0 auto;
    padding: 0;
}

.clear {
    clear: both;
}

.green {
    color: #8aaf0d;
}

#header {
    margin: 0 auto;
    padding: 50px 0;
    text-align: center;
}

#header h1,#header p.online_count {
    font-family: 'Open Sans', sans-serif;
    font-weight: 300;
}

#header p.online_count {
    font-size: 18px;
    display: none;
}

.box_shadow {
    background: #f3f4f6;
    border: 1px solid #e4e4e4;
    -moz-box-shadow: 0px 0px 2px 1px #e5e5e5;
    -webkit-box-shadow: 0px 0px 2px 1px #e5e5e5;
    box-shadow: 0px 0px 2px 1px #e5e5e5;
}

#prompt_name_container {
    margin: 0 auto;
    width: 350px;
    text-align: center;
}

#prompt_name_container p {
    font-family: 'Open Sans', sans-serif;
    font-weight: 300;
    font-size: 24px;
    color: #5e5e5e;
}

#prompt_name_container #input_name {
    border: 1px solid #dddddd;
    padding: 10px;
    width: 250px;
    display: block;
    margin: 0 auto;
    outline: none;
    font-family: 'Open Sans', sans-serif;
}

#prompt_name_container #btn_join {
    border: none;
    width: 100px;
    display: block;
    outline: none;
    font-family: 'Open Sans', sans-serif;
    color: #fff;
    background: #96be0e;
    font-size: 18px;
    padding: 5px 20px;
    margin: 15px auto;
    cursor: pointer;
}

#message_container {
    display: none;
    width: 500px;
    margin: 0 auto;
    background: #fff;
    padding: 15px 0 0 0;
}

#messages {
    margin: 0;
    padding: 0;
    height: 300px;
    overflow: scroll;
    overflow-x: hidden;
}

#messages li {
    list-style: none;
    font-family: 'Open Sans', sans-serif;
    font-size: 16px;
    padding: 10px 20px;
}

#messages li.new,#messages li.exit {
    font-style: italic;
    color: #bbbbbb;
}

#messages li span.name {
    color: #8aaf0d;
}

#messages li span.red {
    color: #e94e59;
}

#input_message_container {
    margin: 40px 20px 0 20px;
}

#input_message {
    background: #f0f0f0;
    border: none;
    font-size: 20px;
    font-family: 'Open Sans', sans-serif;
    outline: none;
    padding: 10px;
    float: left;
    margin: 0;
    width: 348px;
}

#btn_send {
    float: left;
    margin: 0;
    border: none;
    color: #fff;
    font-family: 'Open Sans', sans-serif;
    background: #96be0e;
    outline: none;
    padding: 10px 20px;
    font-size: 20px;
    cursor: pointer;
}

#btn_close {
    margin: 0;
    border: none;
    color: #fff;
    font-family: 'Open Sans', sans-serif;
    background: #e94e59;
    outline: none;
    padding: 10px 20px;
    font-size: 20px;
    cursor: pointer;
    width: 100%;
    margin: 30px 0 0 0;
}

main.js

var sessionId = '';

// name of the client
var name = '';

// socket connection url and port
var socket_url = '192.168.0.102';
var port = '8080';

$(document).ready(function() {

    $("#form_submit, #form_send_message").submit(function(e) {
        e.preventDefault();
        join();
    });
});

var webSocket;

/**
 * Connecting to socket
 */
function join() {
    // Checking person name
    if ($('#input_name').val().trim().length <= 0) {
        alert('Enter your name');
    } else {
        name = $('#input_name').val().trim();

        $('#prompt_name_container').fadeOut(1000, function() {
            // opening socket connection
            openSocket();
        });
    }

    return false;
}

/**
 * Will open the socket connection
 */
function openSocket() {
    // Ensures only one connection is open at a time
    if (webSocket !== undefined && webSocket.readyState !== WebSocket.CLOSED) {
        return;
    }

    // Create a new instance of the websocket
    webSocket = new WebSocket("ws://" + socket_url + ":" + port
            + "/WebMobileGroupChatServer/chat?name=" + name);

    /**
     * Binds functions to the listeners for the websocket.
     */
    webSocket.onopen = function(event) {
        $('#message_container').fadeIn();

        if (event.data === undefined)
            return;

    };

    webSocket.onmessage = function(event) {

        // parsing the json data
        parseMessage(event.data);
    };

    webSocket.onclose = function(event) {
        alert('Error! Connection is closed. Try connecting again.');
    };
}

/**
 * Sending the chat message to server
 */
function send() {
    var message = $('#input_message').val();

    if (message.trim().length > 0) {
        sendMessageToServer('message', message);
    } else {
        alert('Please enter message to send!');
    }

}

/**
 * Closing the socket connection
 */
function closeSocket() {
    webSocket.close();

    $('#message_container').fadeOut(600, function() {
        $('#prompt_name_container').fadeIn();
        // clearing the name and session id
        sessionId = '';
        name = '';

        // clear the ul li messages
        $('#messages').html('');
        $('p.online_count').hide();
    });
}

/**
 * Parsing the json message. The type of message is identified by 'flag' node
 * value flag can be self, new, message, exit
 */
function parseMessage(message) {
    var jObj = $.parseJSON(message);

    // if the flag is 'self' message contains the session id
    if (jObj.flag == 'self') {

        sessionId = jObj.sessionId;

    } else if (jObj.flag == 'new') {
        // if the flag is 'new', a client joined the chat room
        var new_name = 'You';

        // number of people online
        var online_count = jObj.onlineCount;

        $('p.online_count').html(
                'Hello, <span class="green">' + name + '</span>. <b>'
                        + online_count + '</b> people online right now')
                .fadeIn();

        if (jObj.sessionId != sessionId) {
            new_name = jObj.name;
        }

        var li = '<li class="new"><span class="name">' + new_name + '</span> '
                + jObj.message + '</li>';
        $('#messages').append(li);

        $('#input_message').val('');

    } else if (jObj.flag == 'message') {
        // if the json flag is 'message', it means somebody sent the chat
        // message

        var from_name = 'You';

        if (jObj.sessionId != sessionId) {
            from_name = jObj.name;
        }

        var li = '<li><span class="name">' + from_name + '</span> '
                + jObj.message + '</li>';

        // appending the chat message to list
        appendChatMessage(li);

        $('#input_message').val('');

    } else if (jObj.flag == 'exit') {
        // if the json flag is 'exit', it means somebody left the chat room
        var li = '<li class="exit"><span class="name red">' + jObj.name
                + '</span> ' + jObj.message + '</li>';

        var online_count = jObj.onlineCount;

        $('p.online_count').html(
                'Hello, <span class="green">' + name + '</span>. <b>'
                        + online_count + '</b> people online right now');

        appendChatMessage(li);
    }
}

/**
 * Appending the chat message to list
 */
function appendChatMessage(li) {
    $('#messages').append(li);

    // scrolling the list to bottom so that new message will be visible
    $('#messages').scrollTop($('#messages').height());
}

/**
 * Sending message to socket server message will be in json format
 */
function sendMessageToServer(flag, message) {
    var json = '{""}';

    // preparing json object
    var myObject = new Object();
    myObject.sessionId = sessionId;
    myObject.message = message;
    myObject.flag = flag;

    // converting json object to json string
    json = JSON.stringify(myObject);

    // sending message to server
    webSocket.send(json);
}

下载jquery-1.11.1.min

index.html

<!DOCTYPE html>

<html>
<head>
<title>Android, WebSockets Chat App | AndroidHive
    (www.androidhive.info)</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<link
    href='http://fonts.googleapis.com/css?family=Open+Sans:400,600,300,700'
    rel='stylesheet' type='text/css'>
<link href="style.css" type="text/css" rel='stylesheet' />
<script type="text/javascript" src="main.js"></script>
</head>
<body>
    <div class="body_container">

        <div id="header">
            <h1>Android WebSockets Chat Application</h1>
            <p class='online_count'>
                <b>23</b> people online right now
            </p>
        </div>

        <div id="prompt_name_container" class="box_shadow">
            <p>Enter your name</p>
            <form id="form_submit" method="post">
                <input type="text" id="input_name" /> <input type="submit"
                    value="JOIN" id="btn_join">
            </form>
        </div>

        <div id="message_container" class="box_shadow">

            <ul id="messages">
            </ul>


            <div id="input_message_container">
                <form id="form_send_message" method="post" action="#">
                    <input type="text" id="input_message"
                        placeholder="Type your message here..." /> <input type="submit"
                        id="btn_send" onclick="send();" value="Send" />
                    <div class="clear"></div>
                </form>
            </div>
            <div>

                <input type="button" onclick="closeSocket();"
                    value="Leave Chat Room" id="btn_close" />
            </div>

        </div>

    </div>

</body>
</html>

请把main.js中的ip填自己的

var socket_url = '_YOUR_IP_ADDRESS_';

安卓版的请看下一篇

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值