Android Socket 的使用方法

引言

Socket是位于應(yīng)用層和傳輸層之間的一個(gè)抽象層,把TCP/IP層復(fù)雜的操作抽象為幾個(gè)簡(jiǎn)單的接口懊纳,供應(yīng)用層調(diào)用以實(shí)現(xiàn)進(jìn)程在網(wǎng)絡(luò)中通信漠趁。
Socket分為流式套接字和數(shù)據(jù)包套接字慕淡,分別對(duì)應(yīng)網(wǎng)絡(luò)傳輸控制層的TCP協(xié)議和UDP協(xié)議。TCP協(xié)議是一種面向連接的病附,可靠的问窃,基于字節(jié)流的傳輸層通信協(xié)議,它使用三次握手協(xié)議建立連接完沪,并且提供了超時(shí)重傳機(jī)制域庇,具有很高的穩(wěn)定性嵌戈。UDP協(xié)議是一種無連接的協(xié)議,且不對(duì)數(shù)據(jù)包進(jìn)行可靠性保證听皿。
在網(wǎng)絡(luò)差的情況下熟呛,UDP協(xié)議數(shù)據(jù)包丟失會(huì)比較嚴(yán)重,但由于其不屬于連接型協(xié)議尉姨,具有資源消耗少庵朝,處理速度快的優(yōu)點(diǎn),在音頻視頻等傳輸時(shí)使用UDP協(xié)議較多又厉。

示例

這里我們通過socket實(shí)現(xiàn)兩個(gè)進(jìn)程之間的通信九府。

  1. 建立兩個(gè)工程,分別是TestServerSocket和TestClientSocket覆致,并分別增加權(quán)限侄旬。
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  1. TestServerSocket
    當(dāng)server service啟動(dòng)的時(shí)候,創(chuàng)建ServerSocket煌妈,對(duì)端口8688進(jìn)行監(jiān)聽儡羔;然后進(jìn)行while循環(huán),在循環(huán)中阻塞線程直到接收到客戶端消息声旺;收到客戶端消息的時(shí)候會(huì)收到客戶端的Socket笔链,然后開啟while循環(huán)段只,通過InputStream獲取其消息腮猖,通過OutputStream向其發(fā)送消息。
  • MainActivity
    @Override
    protected void onResume() {
        super.onResume();
        startService(new Intent(this,SocketServerService.class));
    }
  • SocketServerService
public class SocketServerService extends Service {
    private boolean isServiceDestroyed = false;
    private static final String TAG = "SocketServerService";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        new Thread(new TcpServer()).start();
    }
    private class TcpServer implements Runnable{
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try{
                serverSocket = new ServerSocket(8688);
            }catch (IOException exception){
                exception.printStackTrace();
            }
            while(!isServiceDestroyed){
                try {
                    if (serverSocket == null){
                        return;
                    }
                    Log.i(TAG, "ServerSocket loop listen ClientSocket");
                    //接收客戶端的請(qǐng)求赞枕,并且阻塞直到接收到消息
                    final Socket client = serverSocket.accept();
                    new Thread(() -> {
                        try {
                            responseClient(client);
                        } catch (IOException exception) {
                            exception.printStackTrace();
                        }
                    }).start();
                }catch (IOException exception){
                    exception.printStackTrace();
                }
            }

        }
    }
    private void responseClient(Socket client) throws IOException{
        // 用于接收客戶端的消息
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        // 用于向客戶端發(fā)送消息
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                client.getOutputStream())),true);
        out.println("你好澈缺,我是服務(wù)端");
        while(!isServiceDestroyed){
            String str = in.readLine();
            Log.i(TAG, "收到客戶端發(fā)來的消息" + str);
            if (TextUtils.isEmpty(str)){
                //客戶端斷開了連接
                Log.i(TAG, "客戶端斷開了連接");
                break;
            }
            String message = "收到了客戶端的消息為:" + str;
            // 從客戶端收到的消息加工再發(fā)送給客戶端
            out.println(message);
        }
        out.close();
        in.close();
        client.close();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isServiceDestroyed = true;
    }
}

3.TestClientSocket
客戶端首先就是開啟while循環(huán),創(chuàng)建Socket與ServerSocket進(jìn)行連接炕婶,直到建立與ServerSocket的連接姐赡;然后同樣是獲得ServerSocket,通過InputStream讀取其內(nèi)容柠掂,通過OutputStream向其寫入內(nèi)容项滑。

public class MainActivity extends AppCompatActivity {

    private PrintWriter mPrintWriter;
    private EditText mEt_receive;
    private TextView mTv_message;
    private static final String TAG = "TestClientService";
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message message) {
            mTv_message.setText(String.format("%s\n客戶端:%s", mTv_message.getText(), message.obj));
            mEt_receive.setText("");
            return true;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        new Thread(this::connectSocketServer).start();
    }

    private void initView() {
        mEt_receive = findViewById(R.id.et_receive);
        Button mBt_send = findViewById(R.id.bt_send);
        mTv_message = findViewById(R.id.tv_message);
        mBt_send.setOnClickListener(view -> {
            final String msg = mEt_receive.getText().toString();
            //向服務(wù)器發(fā)送消息
            if (!TextUtils.isEmpty(msg) && null!=mPrintWriter){
                new Thread(() -> {
                    mPrintWriter.println(msg);
                    Message message = Message.obtain();
                    message.what = 0;
                    message.obj = msg;
                    mHandler.sendMessage(message);
                }).start();
            }
        });
    }

    private void connectSocketServer() {
        Socket socket = null;
        while (socket == null){
            try {
                //選擇和服務(wù)器相同的ip和端口 8688
                socket = new Socket("192.168.1.3",8688);
                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                        socket.getOutputStream())),true);
            }catch (IOException exception){
                Log.d(TAG, "connectSocketServer: " + exception);
                SystemClock.sleep(1000);
            }
        }
        try {
            // 用于接收服務(wù)端的消息
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while(!isFinishing()){
                final String msg = br.readLine();
                if (msg != null){
                    runOnUiThread(() ->
                            mTv_message.setText(
                            String.format("%s\n服務(wù)端:%s", mTv_message.getText(), msg)));
                }
            }
            mPrintWriter.close();
            br.close();
            socket.close();
        }catch (IOException exception){
            exception.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
        mHandler = null;
    }
}
  1. 先打開服務(wù)端
2022-04-30 ... com.example.testserversocket I/SocketServerService: ServerSocket loop listen ClientSocket

4.再打開客戶端
客戶端開啟之后建立與服務(wù)端的連接。


客戶端建立了與服務(wù)端的連接.jpg

連接創(chuàng)建好之后涯贞,客戶端向服務(wù)端發(fā)送信息枪狂。


服務(wù)端收到了客戶端發(fā)送的信息.jpg
2022-04-30 ... com.example.testserversocket I/SocketServerService: 收到客戶端發(fā)來的消息你好,我是客戶端

總結(jié)

在上層宋渔,socket基于對(duì)相同IP和相同端口的監(jiān)聽實(shí)現(xiàn)的州疾。

參考

劉望舒的Android進(jìn)階指北。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末皇拣,一起剝皮案震驚了整個(gè)濱河市严蓖,隨后出現(xiàn)的幾起案子薄嫡,更是在濱河造成了極大的恐慌,老刑警劉巖颗胡,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毫深,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡毒姨,警方通過查閱死者的電腦和手機(jī)费什,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來手素,“玉大人鸳址,你說我怎么就攤上這事∪常” “怎么了稿黍?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)崩哩。 經(jīng)常有香客問我巡球,道長(zhǎng),這世上最難降的妖魔是什么邓嘹? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任酣栈,我火速辦了婚禮,結(jié)果婚禮上汹押,老公的妹妹穿的比我還像新娘矿筝。我一直安慰自己,他們只是感情好棚贾,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布窖维。 她就那樣靜靜地躺著,像睡著了一般妙痹。 火紅的嫁衣襯著肌膚如雪铸史。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天怯伊,我揣著相機(jī)與錄音琳轿,去河邊找鬼。 笑死耿芹,一個(gè)胖子當(dāng)著我的面吹牛崭篡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播猩系,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼媚送,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了寇甸?” 一聲冷哼從身側(cè)響起塘偎,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤疗涉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后吟秩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咱扣,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年涵防,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闹伪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡壮池,死狀恐怖偏瓤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椰憋,我是刑警寧澤厅克,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站橙依,受9級(jí)特大地震影響证舟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窗骑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一女责、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧创译,春花似錦抵知、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至互订,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痘拆,已是汗流浹背仰禽。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纺蛆,地道東北人吐葵。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桥氏,于是被迫代替她去往敵國(guó)和親温峭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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