Android 網(wǎng)絡(luò)(二) socket

一叠萍、一臺(tái)手機(jī)向另外一臺(tái)手機(jī)發(fā)送消息,類似QQ

參考
Android socket 編程 實(shí)現(xiàn)消息推送(一)
Android socket 編程 實(shí)現(xiàn)消息推送(二)

手機(jī)1
手機(jī)2

實(shí)現(xiàn)過程主要分為Server端和Client端巧颈,Server端采用Java的編程肪笋,而Client端則用Android編程俊性。所以在這里也分別創(chuàng)建了兩個(gè)工程SocketServer和SocketClient蓖谢。

1.SocketServer

SocketServer

SocketMessage.java:

public class SocketMessage {
    public int to;//socketID乳规,指發(fā)送給誰
    public int from;//socketID锤岸,指誰發(fā)送過來的
    public String msg;//消息內(nèi)容
    public String time;//接收時(shí)間
    public SocketThread thread;//socketThread下面有介紹
}

MyServer.java:

package com.jimstin.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import org.json.JSONObject;


import com.jimstin.msg.SocketMessage;

public class MyServer {

    private boolean isStartServer;
    private ServerSocket mServer;
    /**
     * 消息隊(duì)列集漾,用于保存SocketServer接收來自于客戶機(jī)(手機(jī)端)的消息
     */
    private ArrayList<SocketMessage> mMsgList = new ArrayList<SocketMessage>();
    /**
     * 線程隊(duì)列,用于接收消息锉罐。每個(gè)客戶機(jī)擁有一個(gè)線程,每個(gè)線程只接收發(fā)送給自己的消息
     */
    private ArrayList<SocketThread> mThreadList = new ArrayList<SocketThread>();
    
    /**
     * 開啟SocketServer
     */
    private void startSocket() {
        try {
            isStartServer = true;
            int prot = 2000;//端口可以自己設(shè)置绕娘,但要和Client端的端口保持一致
            mServer = new ServerSocket(prot);//創(chuàng)建一個(gè)ServerSocket
            System.out.println("啟動(dòng)server,端口:"+prot);
            Socket socket = null;
            int socketID = 0;//Android(SocketClient)客戶機(jī)的唯一標(biāo)志脓规,每個(gè)socketID表示一個(gè)Android客戶機(jī)
            //開啟發(fā)送消息線程
            startSendMessageThread();
            //用一個(gè)循環(huán)來檢測(cè)是否有新的客戶機(jī)加入
            while(isStartServer) {
                //accept()方法是一個(gè)阻塞的方法,調(diào)用該方法后险领,
                //該線程會(huì)一直阻塞侨舆,直到有新的客戶機(jī)加入,代碼才會(huì)繼續(xù)往下走
                socket = mServer.accept();
                //有新的客戶機(jī)加入后绢陌,則創(chuàng)建一個(gè)新的SocketThread線程對(duì)象
                SocketThread thread = new SocketThread(socket, socketID++);
                thread.start();
                //將該線程添加到線程隊(duì)列
                mThreadList.add(thread);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 開啟推送消息線程挨下,如果mMsgList中有SocketMessage,則把該消息推送到Android客戶機(jī)
     */
    public void startSendMessageThread() {
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    /*如果isStartServer=true脐湾,則說明SocketServer已啟動(dòng)臭笆,
                    用一個(gè)循環(huán)來檢測(cè)消息隊(duì)列中是否有消息,如果有秤掌,則推送消息到相應(yīng)的客戶機(jī)*/
                    while(isStartServer) {
                        //判斷消息隊(duì)列中的長(zhǎng)度是否大于0愁铺,大于0則說明消息隊(duì)列不為空
                        if(mMsgList.size() > 0) {
                            //讀取消息隊(duì)列中的第一個(gè)消息
                            SocketMessage from = mMsgList.get(0);
                            for(SocketThread to : mThreadList) {
                                if(to.socketID == from.to) {
                                    BufferedWriter writer = to.writer;
                                    JSONObject json = new JSONObject();
                                    json.put("from", from.from);
                                    json.put("msg", from.msg);
                                    json.put("time", from.time);
                                    //writer寫進(jìn)json中的字符串?dāng)?shù)據(jù),末尾記得加換行符:"\n"闻鉴,否則在客戶機(jī)端無法識(shí)別
                                    //因?yàn)锽ufferedReader.readLine()方法是根據(jù)換行符來讀取一行的
                                    writer.write(json.toString()+"\n");
                                    //調(diào)用flush()方法茵乱,刷新流緩沖,把消息推送到手機(jī)端
                                    writer.flush();
                                    System.out.println("推送消息成功:"+from.msg+">> to socketID:"+from.to);
                                    break;
                                }
                            }
                            //每推送一條消息之后孟岛,就要在消息隊(duì)列中移除該消息
                            mMsgList.remove(0);
                        }
                        Thread.sleep(200);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
    
    /**
     * 定義一個(gè)SocketThread類瓶竭,用于接收消息
     *
     */
    public class SocketThread extends Thread {
        
        public int socketID;
        public Socket socket;//Socket用于獲取輸入流、輸出流
        public BufferedWriter writer;//BufferedWriter 用于推送消息
        public BufferedReader reader;//BufferedReader 用于接收消息
        
        public SocketThread(Socket socket, int count) {
            socketID = count;
            this.socket = socket;
            System.out.println("新增一臺(tái)客戶機(jī)渠羞,socketID:"+socketID);
        }
        
        @Override
        public void run() {
            super.run();

            try {
                //初始化BufferedReader
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
                //初始化BufferedWriter
                writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
                //如果isStartServer=true斤贰,則說明SocketServer已經(jīng)啟動(dòng),
                //現(xiàn)在需要用一個(gè)循環(huán)來不斷接收來自客戶機(jī)的消息次询,并作其他處理
                while(isStartServer) {
                    //先判斷reader是否已經(jīng)準(zhǔn)備好
                    if(reader.ready()) {
                        /*讀取一行字符串腋舌,讀取的內(nèi)容來自于客戶機(jī)
                        reader.readLine()方法是一個(gè)阻塞方法,
                        從調(diào)用這個(gè)方法開始渗蟹,該線程會(huì)一直處于阻塞狀態(tài)块饺,
                        直到接收到新的消息,代碼才會(huì)往下走*/
                        String data = reader.readLine();
                        //講data作為json對(duì)象的內(nèi)容雌芽,創(chuàng)建一個(gè)json對(duì)象
                        JSONObject json = new JSONObject(data);
                        //創(chuàng)建一個(gè)SocketMessage對(duì)象授艰,用于接收json中的數(shù)據(jù)
                        SocketMessage msg = new SocketMessage();
                        msg.to = json.getInt("to");
                        msg.msg = json.getString("msg");
                        msg.from = socketID;
                        msg.time = getTime(System.currentTimeMillis());
                        //接收到一條消息后,將該消息添加到消息隊(duì)列mMsgList
                        mMsgList.add(msg);
                        System.out.println("收到一條消息:"+json.getString("msg")+" >>>> to socketID:"+json.getInt("to"));
                    }
                    //睡眠100ms世落,每100ms檢測(cè)一次是否有接收到消息
                    Thread.sleep(100);
                }
                
            } catch (Exception e) {
                e.printStackTrace();
            }  
            
        }
    }
    /**
     * 獲取指定格式的時(shí)間字符串淮腾,通過毫秒轉(zhuǎn)換日期
     * @param millTime
     */
    private String getTime(long millTime) {
        Date d = new Date(millTime);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(d);
    }
    public static void main(String[] args) {
        MyServer server = new MyServer();
        server.startSocket();
    }

}

2.SocketClient工程

SocketClient
package com.jimstin.socketclient;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.json.JSONObject;

import com.tencent.stat.MtaSDkException;
import com.tencent.stat.StatConfig;
import com.tencent.stat.StatService;

import android.R.integer;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.app.Activity;

public class MainActivity extends Activity implements OnClickListener {

    private EditText mIPEdt, mPortEdt, mSocketIDEdt, mMessageEdt;
    private static TextView mConsoleTxt;
    
    private static StringBuffer mConsoleStr = new StringBuffer();
    private Socket mSocket;
    private boolean isStartRecieveMsg;
    
    private SocketHandler mHandler;
    protected BufferedReader mReader;//BufferedWriter 用于推送消息
    protected BufferedWriter mWriter;//BufferedReader 用于接收消息
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mIPEdt = (EditText) findViewById(R.id.ip_edt);
        mPortEdt = (EditText) findViewById(R.id.port_edt);
        mSocketIDEdt = (EditText) findViewById(R.id.socket_id_edt);
        mMessageEdt = (EditText) findViewById(R.id.msg_edt);
        mConsoleTxt = (TextView) findViewById(R.id.console_txt);
        findViewById(R.id.start_btn).setOnClickListener(this);
        findViewById(R.id.send_btn).setOnClickListener(this);
        findViewById(R.id.clear_btn).setOnClickListener(this);
        mHandler = new SocketHandler();
    }

    /**
     * 初始化socket
     */
    private void initSocket() {
        //新建一個(gè)線程,用于初始化socket和檢測(cè)是否有接收到新的消息
        Thread thread = new Thread(new Runnable() {
            
            @Override
            public void run() {
                String ip = mIPEdt.getText().toString();//IP
                int port = Integer.parseInt(mPortEdt.getText().toString());//Socket
                
                try {
                    isStartRecieveMsg = true;
                    mSocket = new Socket(ip, port);
                    mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "utf-8"));
                    mWriter = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream(), "utf-8"));
                    while(isStartRecieveMsg) {
                        if(mReader.ready()) {
                            /*讀取一行字符串,讀取的內(nèi)容來自于客戶機(jī)
                            reader.readLine()方法是一個(gè)阻塞方法谷朝,
                            從調(diào)用這個(gè)方法開始洲押,該線程會(huì)一直處于阻塞狀態(tài),
                            直到接收到新的消息圆凰,代碼才會(huì)往下走*/
                            String data = mReader.readLine();
                            //handler發(fā)送消息杈帐,在handleMessage()方法中接收
                            mHandler.obtainMessage(0, data).sendToTarget();
                        }
                        Thread.sleep(200);
                    }
                    mWriter.close();
                    mReader.close();
                    mSocket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                } 
            }
        });
        thread.start();
    }
    
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.send_btn:
            send();
            break;
        case R.id.clear_btn:
            mConsoleStr.delete(0, mConsoleStr.length());
            mConsoleTxt.setText(mConsoleStr.toString());
            break;
        case R.id.start_btn:
            if(!isStartRecieveMsg) {
                initSocket();
            }
            break;
        default:
            break;
        }
    }

    /**
     * 發(fā)送
     */
    private void send() {
        new AsyncTask<String, Integer, String>() {

            @Override
            protected String doInBackground(String... params) {
                sendMsg();
                return null;
            }
        }.execute();
    }
    /**
     * 發(fā)送消息
     */
    protected void sendMsg() {
        try {
            String socketID = mSocketIDEdt.getText().toString().trim();
            String msg = mMessageEdt.getText().toString().trim();
            JSONObject json = new JSONObject();
            json.put("to", socketID);
            json.put("msg", msg);
            mWriter.write(json.toString()+"\n");
            mWriter.flush();
            mConsoleStr.append("我:" +msg+"   "+getTime(System.currentTimeMillis())+"\n");
            mConsoleTxt.setText(mConsoleStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class SocketHandler extends Handler {
        
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            switch (msg.what) {
            case 0:
                try {
                    //將handler中發(fā)送過來的消息創(chuàng)建json對(duì)象
                    JSONObject json = new JSONObject((String)msg.obj);
                    mConsoleStr.append(json.getString("from")+":" +json.getString("msg")
                    +"   "+getTime(System.currentTimeMillis())+"\n");
                    //將json數(shù)據(jù)顯示在TextView中
                    mConsoleTxt.setText(mConsoleStr);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                
                break;

            default:
                break;
            }
        }
    }
    
    @Override
    public void onBackPressed() {
        super.onBackPressed();
        isStartRecieveMsg = false;
    }
    
    private static String getTime(long millTime) {
        Date d = new Date(millTime);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(d);
    }
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市专钉,隨后出現(xiàn)的幾起案子挑童,更是在濱河造成了極大的恐慌,老刑警劉巖跃须,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件站叼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡菇民,警方通過查閱死者的電腦和手機(jī)尽楔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來第练,“玉大人翔试,你說我怎么就攤上這事「囱” “怎么了垦缅?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)驹碍。 經(jīng)常有香客問我壁涎,道長(zhǎng),這世上最難降的妖魔是什么志秃? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任怔球,我火速辦了婚禮,結(jié)果婚禮上浮还,老公的妹妹穿的比我還像新娘竟坛。我一直安慰自己,他們只是感情好钧舌,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布担汤。 她就那樣靜靜地躺著,像睡著了一般洼冻。 火紅的嫁衣襯著肌膚如雪崭歧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天撞牢,我揣著相機(jī)與錄音率碾,去河邊找鬼叔营。 笑死,一個(gè)胖子當(dāng)著我的面吹牛所宰,可吹牛的內(nèi)容都是我干的绒尊。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼仔粥,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼婴谱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起件炉,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤勘究,失蹤者是張志新(化名)和其女友劉穎矮湘,沒想到半個(gè)月后斟冕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缅阳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年磕蛇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十办。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秀撇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出向族,到底是詐尸還是另有隱情呵燕,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布件相,位于F島的核電站再扭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夜矗。R本人自食惡果不足惜泛范,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望紊撕。 院中可真熱鬧罢荡,春花似錦、人聲如沸对扶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浪南。三九已至惧笛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逞泄,已是汗流浹背患整。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國(guó)打工拜效, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人各谚。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓紧憾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親昌渤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赴穗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,348評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)膀息,斷路器般眉,智...
    卡卡羅2017閱讀 134,720評(píng)論 18 139
  • 上回我們講到了dmzj漫畫搜索爬蟲(二),緊接著上一回的代碼潜支,我們繼續(xù)進(jìn)行深入的爬取分析甸赃,完成對(duì)于漫畫的圖片爬取。...
    淺淺的笑意閱讀 3,704評(píng)論 4 5
  • 【導(dǎo)語】兄弟一生一起走,那些日子不再有裁替!也許兄弟在身邊项玛,男人就更有一份自信,可并不是每個(gè)兄弟都是能夠陪伴你到最后的...
    靦腆的蘑菇魚閱讀 492評(píng)論 0 0
  • 過去弱判,收藏者的目光主要集中在字畫和瓷器等主流收藏種類上襟沮,根雕藝術(shù)品受到冷落,一些根雕珍品也很難賣出高價(jià)。但是近年來...
    瘋狂的木頭閱讀 373評(píng)論 2 5