Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android學習筆記037之基於TCP的socket通信

Android學習筆記037之基於TCP的socket通信

編輯:關於Android編程

我們說過Android中客戶端與服務端通信有兩種方式,HTTP通信和Socket通信,前面我們介紹過HTTP通信了,現在,我們來學習一下Socket通信。學習Socket之前,我們需要先學習一下TCP/IP協議和UDP協議。

1、Socket基本通信模型

我們需要學習的有兩種網絡通信參考模型,分別是TCP/IP參考模型和OSI參考模型,下面我們分別學習一下這兩種參考模型:

TCP/IP參考模型

TCP/IP參考模型是計算機網絡的祖父ARPANET和其後繼的因特網使用的參考模型。TCP/IP參考模型將協議分為以下的四個層次:

應用層:主要為用戶提供各種服務,例如:Telnet、FTP、DNS等,數據傳輸單位是:數據段

傳輸層:應用層實體提供端到端的通信功能,保證了數據包的順序傳送及數據的完整性。該層定義了兩個主要的協議:傳輸控制協議(TCP)和用戶數據報協議(UDP).數據傳輸單位是:數據包

網際互聯層:主要解決主機到主機的通信問題。它所包含的協議設計數據包在整個網絡上的邏輯傳輸。注重重新賦予主機一個IP地址來完成對主機的尋址,它還負責數據包在多種網絡中的路由。該層有三個主要協議:網際協議(IP)、互聯網組管理協議(IGMP)和互聯網控制報文協議(ICMP)。數據傳輸單位是:幀

網絡接入層:負責監視數據在主機和網絡之間的交換。事實上,TCP/IP本身並未定義該層的協議,而由參與互連的各網絡使用自己的物理層和數據鏈路層協議,然後與TCP/IP的網絡接入層進行連接。數據傳輸單位是:比特

OSI參考模型

OSI(Open System Interconnect)開放式系統互聯,是ISO(國際標准化組織)組織在1985年研究的網絡互聯模型。該體系結構標准定義了網絡互連的七層框架(物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層),即ISO開放系統互連參考模型。

\

第7層應用層:OSI中的最高層。為特定類型的網絡應用提供了訪問OSI環境的手段。應用層確定進程之間通信的性質,以滿足用戶的需要。應用層不僅要提供應用進程所需要的信息交換和遠程操作,而且還要作為應用進程的用戶代理,來完成一些為進行信息交換所必需的功能。它包括:文件傳送訪問和管理FTAM、虛擬終端VT、事務處理TP、遠程數據庫訪問RDA、制造報文規范MMS、目錄服務DS等協議;應用層能與應用程序界面溝通,以達到展示給用戶的目的。 在此常見的協議有:HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。

第6層表示層:主要用於處理兩個通信系統中交換信息的表示方式。為上層用戶解決用戶信息的語法問題。它包括數據格式交換、數據加密與解密、數據壓縮與終端類型的轉換。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrXaNbLju+G7sLLjo7rU2sG9uPa92rXj1q685L2owaK2y8GsvdOho86qtsvPtc2ztcTTptPDs8zQ8tauvOTM4bmpwcu21Luwv9jWxrv61saho7TLt/7O8bD8wKi9qMGiway908rH0tTIq8uruaS7ucrH0tSw68uruaS1xLe9yr29+NDQyejWw6OsvqG53L/J0tTU2rLjNNbQtKbA7curuaS3vcq9IKO7u+G7sLLjudzA7bXHyOu6zdeiz/q5/bPMoaPL/L7fzOW53MDtwb249tPDu6e6zb34s8zWrrzktcS21LuwoaPI57n71NrEs9K7yrG/zNa71MrQ7dK7uPbTw7un1rTQ0NK7z+7M2LaotcSy2df3o6y74buwsuPQrdLpvs274bncwO3V4tCpstnX96OsyOfX6Na5wb249tPDu6fNrMqxuPzQwsr9vt2/4tbQtcTNrNK71+nK/b7doaM8L3A+DQo8cD612jSy47SryuSy46O6Jm1kYXNoO7OjuebK/b7dtd3LzaOtw+bP8sGsvdO78s7eway906Gjzqq74buwsuPTw7unzOG5qdK7uPa2y7W9tsu1xL/Jv7+hos24w/e6zdPFu6+1xMr9vt20q8rkt/7O8bv61saho7D8wKjIq8uruaS78rDry6u5pKGiwfe/2NbGus207c7zu9a4tLf+zvGju7SryuSy47DRz/vPorfWs8nI9LjJuPa31tfpo6yyotTavdPK1bbLttTL/MPHvfjQ0NbY1+mho7K7zay1xLfW1+m/ydLUzai5/bK7zay1xMGsvdO0q8vNtb3W97v6oaPV4tH5vMjE3LvxtcO9z7jftcS0+L/to6zT1rK707DP7Lvhu7Cy46Gj1Nq9qMGiway908qxtKvK5LLjv8nS1Mfrx/O3/s7x1srBv6OsuMO3/s7x1srBv9a4tqi/yb3Tyty1xM7zwuvCyqGi0dOz2cG/oaKwssir0NS1yLLOyv2jrLu5v8nS1Mq1z9a7+dPatsu1vbbLtcTB98G/v9jWxrmmxNyhozwvcD4NCjxwPrXaM7LjzfjC57Ljo7qxvrLjzai5/dGw1rfAtL2owaLBvbj2vdq149auvOS1xMGsvdOjrM6q1LS2y7XE1MvK5LLjy83AtLXEt9bX6aOs0aHU8brPysq1xMK308m6zb27u7u92rXjo6zV/ci3zt7O87XYsLTV1bXY1re0q8vNuPjEv7XEtsu1xNTLyuSy46Gjy/yw/MCozai5/bulwazN+MLnwLTCt9PJus3W0LzMyv2+3SCju7P9wcvRodTxwrfTydauzeKjrM34wuey47u5uLrU8L2owaK6zc6su6TBrL3To6y/2NbGzfjC58nPtcTTtcj70tS8sNTasdjSqrXEyrG68sn6s8m8xrfR0MXPoqGjPC9wPg0KPHA+tdoysuPK/b7dwbTCt7Ljo7rU2rTLsuO9q8r9vt231taho6yyorSmwO3B97/Y1saho8bBsc7O78DtsuOjrM6qzfjC57LjzOG5qdK7uPbK/b7dwbTCt7XEway906Os1NrSu8z109C/ycTcs/ay7rTttcTO78Dtway908nPo6y9+NDQvLi69c7esu607bXEyv2+3bSryuSjqLLutO2/2NbGo6mho7G+suPWuLaozdjGy73hubmyoszhuanTsrz+0bDWt6Gjs6PTw8nosbjT0M34v6ihos34x8Whor27u7u7+qO7PC9wPg0KPHA+tdoxsuPO78DtsuOjurSm09pPU0myzr+8xKPQzbXE1+6117LjoaPO78DtsuO1xNb30qq5psTcysfA+9PDzu/A7bSryuS96dbKzqrK/b7dwbTCt7LjzOG5qc7vwO3BrL3To6zS1LHjzbjD97XEtKvLzbHIzNjB96Gjs6PTw8nosbjT0KOouPfW1s7vwO3J6LG4o6m8r8/fxvehotbQvMzG96GitffWxr3itffG96GizfjP36Giy6u9ys/foaLNrNbhtefAwqGjPC9wPg0KPHA+yv2+3beiy83KsaOstNO12sbfsuO0q7W9tdrSu7Ljo6y908rVyv2+3dTyz+C3tKGjyc/I/bLj19yzxtOm08Oy46Os08PAtL/Y1sbI7bz+t73D5qGjz8LLxLLj19yzxsr9vt3B97Ljo6zTw8C0udzA7dOyvP6ho7P9wcvO78DtsuPWrs3ixuTL+7LjtrzKx9PDyO28/sq1z9a1xKGjyv2+3dTat6LWwcr9vt3B97LjtcTKsbryvauxu7Lwt9aho9TatKvK5LLjtcTK/b7dvdC2zqOszfjC57LjvdCw/KOsyv2+3cG0wrey473Q1qGjrM7vwO2y473QscjM2MH3o6zV4tH5tcS90LeovdBQRFWjqNCt0unK/b7dtaXUqqOpPC9wPg0KPGg0IGlkPQ=="兩種參考模型比較">兩種參考模型比較

兩種參考模型比較圖

\

共同點

(1)OSI參考模型和TCP/IP參考模型都采用了層次結構的概念。

(2)都能夠提供面向連接和無連接兩種通信服務機制。

不同點

(1)OSI采用的七層模型,而TCP/IP是四層結構。

(2)TCP/IP參考模型的網絡接口層實際上並沒有真正的定義,只是一些概念性的描述。而OSI參考模型不僅分了兩層,而且每一層的功能都很詳盡,甚至在數據鏈路層又分出一個介質訪問子層,專門解決局域網的共享介質問題。

(3)OSI模型是在協議開發前設計的,具有通用性。TCP/IP是先有協議集然後建立模型,不適用於非TCP/IP網絡。

(4)OSI參考模型與TCP/IP參考模型的傳輸層功能基本相似,都是負責為用戶提供真正的端對端的通信服務,也對高層屏蔽了底層網絡的實現細節。所不同的是TCP/IP參考模型的傳輸層是建立在網絡互聯層基礎之上的,而網絡互聯層只提供無連接的網絡服務,所以面向連接的功能完全在TCP協議中實現,當然TCP/IP的傳輸層還提供無連接的服務,如UDP;相反OSI參考模型的傳輸層是建立在網絡層基礎之上的,網絡層既提供面向連接的服務,又提供無連接的服務,但傳輸層只提供面向連接的服務。

(5)OSI參考模型的抽象能力高,適合與描述各種網絡;而TCP/IP是先有了協議,才制定TCP/IP模型的。

(6)OSI參考模型的概念劃分清晰,但過於復雜;而TCP/IP參考模型在服務、接口和協議的 區別上不清楚,功能描述和實現細節混在一起。

(7)TCP/IP參考模型的網絡接口層並不是真正的一層;OSI參考模型的缺點是層次過多,劃分意義不大但增加了復雜性。

(8)OSI參考模型雖然被看好,由於沒把握好時機,技術不成熟,實現困難;相反,TCP/IP參考模型雖然有許多不盡人意的地方,但還是比較成功的。

2、TCP/IP協議簡介

TCP/IP(transmission Control Protocol/Internet Protocol)傳輸控制協議/互聯網絡協議,是一種網絡通信協議,它規范了網絡上的所有通信設備,尤其是一個主機與另一個主機之間的數據往來格式以及傳送方式。舉個簡單的例子就是:TCP和IP就像兩個信封,我們把要傳輸的數據分割開,放入到TCP信封裡面,TCP信封裡面會記錄有分段號;然後將TCP信封裝入IP這個大信封裡面,傳輸到網絡上。在接收端,一個TCP軟件包收集信封,抽出數據,按發送前的順序還原,並加以校驗,若發現差錯,TCP將會要求重發。
  

TCP/IP網絡連接需要經過三次握手,斷開連接需要三次揮手,在這裡就不在做很詳細的描述了,推薦一篇博文:TCP/IP三次握手與四次揮手 ,這裡已經很詳細的描述三次握手和四次揮手的過程,建議大家一定要閱讀並理解,面試經常會被問到。關於TCP/IP協議就簡單介紹到這裡,我們做應用開發不是網絡工程師,不需要做很深入的學習。

3、UDP協議簡介

前面我們介紹了TCP/IP協議,接著我們介紹一下UDP協議。UDP(User Datagram Protocol)用戶數據報協議,是OSI(Open System Interconnection,開放式系統互聯) 參考模型中一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務,IETF RFC 768是UDP的正式規范。UDP在IP報文的協議號是17。

UDP協議全稱是用戶數據報協議 ,在網絡中它與TCP協議一樣用於處理數據包,是一種無連接的協議。在OSI模型中,在第四層——傳輸層,處於IP協議的上一層。UDP有不提供數據包分組、組裝和不能對數據包進行排序的缺點,也就是說,當報文發送之後,是無法得知其是否安全完整到達的。UDP用來支持那些需要在計算機之間傳輸數據的網絡應用。包括網絡視頻會議系統在內的眾多的客戶/服務器模式的網絡應用都需要使用UDP協議。UDP協議從問世至今已經被使用了很多年,雖然其最初的光彩已經被一些類似協議所掩蓋,但是即使是在今天UDP仍然不失為一項非常實用和可行的網絡傳輸層協議。

與所熟知的TCP(傳輸控制協議)協議一樣,UDP協議直接位於IP(網際協議)協議的頂層。根據OSI(開放系統互連)參考模型,UDP和TCP都屬於傳輸層協議。UDP協議的主要作用是將網絡數據流量壓縮成數據包的形式。一個典型的數據包就是一個二進制數據的傳輸單位。每一個數據包的前8個字節用來包含報頭信息,剩余字節則用來包含具體的傳輸數據。

4、Socket基本概念

前面介紹的都是概念性的東西,非常枯燥,下面正式開始學習基於TCP的Socket通信。

什麼是Socket?稱作”套接字”,用於描述IP地址和端口,是一個通信鏈的句柄,可以用來實現不同虛擬機或不同計算機之間的通信。在Internet上的主機一般運行了多個服務軟件,同時提供幾種服務。每種服務都打開一個Socket,並綁定到一個端口上,不同的端口對應於不同的服務。Socket通常用來實現客戶方和服務方的連接。Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號唯一確定。

Socket通信模型

\

5、Socket的通信流程

Server端Listen(監聽)某個端口是否有連接請求,Client端向Server 端發出Connect(連接)請求,Server端向Client端發回Accept(接受)消息。一個連接就建立起來了。Server端和Client 端都可以通過Send,Write等方法與對方通信。

對於一個功能齊全的Socket,都要包含以下基本結構,其工作過程包含以下四個基本的步驟:

1、創建ServerSocket和Socket

2、打開連接到的Socket的輸入/輸出流

3、按照協議對Socket進行讀/寫操作

4、關閉輸入輸出流,以及Socket

6、Socket搭建簡單的聊天室

下面我們通過Socket搭建一個簡單聊天室來體會一下Socket通信:

首先是服務端的編寫,服務端編寫步驟:

創建ServerSocket對象,綁定監聽的端口

通過調用accept()方法監聽客戶端的請求

建立連接之後,通過輸入流讀取客戶端的請求信息

獲取輸出流向客戶端輸出信息

關閉連接

客戶端的編寫步驟:

創建Socket對象,指明需要連接的服務器的地址和端號

連接建立後,通過輸出流向服務器發送請求信息

通過輸出流獲取服務器響應的信息

關閉連接,釋放相關資源

具體實例代碼:

服務端:

package com.example.socket;

import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Devin on 2016/7/28.
 */
public class SocketServer {
public static boolean isServerStart = true;
private ServerSocket serverSocket;
private static int LISTENER_PART = 10010;
public List socketList = new ArrayList<>();
private ExecutorService executorService;

public void startServer() {
    try {
        serverSocket = new ServerSocket(LISTENER_PART);
        executorService = Executors.newCachedThreadPool();
        InetAddress address = InetAddress.getLocalHost();
        System.out.println("服務器的IP地址是:" + address.getHostAddress() + ",監聽的端口號是:" + LISTENER_PART);
        System.out.println("--------------服務器啟動,等待連接-----------------");
        Socket socket = null;
        while (isServerStart) {
            System.out.println("有用戶登錄進來了");
            socket = serverSocket.accept();
            socketList.add(socket);
            executorService.execute(new ServerThread(socket));
        }

        serverSocket.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

public static void main(String[] args) {
    SocketServer socketServer = new SocketServer();
    socketServer.startServer();
}

}

客戶端:

package com.example.socketdemo;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.example.socketdemo.activity.FriendsActivity;
import com.example.socketdemo.comm.Constans;
import com.example.socketdemo.comm.ServerConn;
import com.example.socketdemo.comm.ToastUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

public class MainActivity extends AppCompatActivity {
private EditText et_user_id;
private EditText et_user_name;
private Button btn_user_login;

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Bundle bundle = msg.getData();
        String isSuccess = bundle.getString("isSuccess");
        int userID = bundle.getInt("userID");
        if (isSuccess.equals("success")) {
            Intent intent = new Intent();
            intent.setClass(MainActivity.this, FriendsActivity.class);
            intent.putExtra("userID", userID);
            startActivity(intent);
        } else {
            ToastUtils.showToast(MainActivity.this, "登錄失敗");
            return;
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    et_user_id = (EditText) findViewById(R.id.et_user_id);
    et_user_name = (EditText) findViewById(R.id.et_user_name);
    btn_user_login = (Button) findViewById(R.id.btn_user_login);
    btn_user_login.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String userID = et_user_id.getText().toString().trim();
            if (userID != null && !userID.equals("")) {
                login(Integer.parseInt(userID));
            } else {
                ToastUtils.showToast(MainActivity.this, "請輸入用戶ID");
                return;
            }
        }
    });
}

private void login(int userID) {
    final int id = userID;
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Socket socket = new Socket();
                socket.connect(new InetSocketAddress(Constans.SERVER_ADDRESS, Constans.SERVER_PORT), 5000);
                InetAddress address = InetAddress.getLocalHost();

                PrintWriter writer = new PrintWriter(socket.getOutputStream());
                writer.println(Constans.TAG_LOGIN + ":" + id + ":" + address.getHostAddress());
                writer.flush();


                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String isSuccess = bufferedReader.readLine();

                ServerConn.mSocket = socket;
                Message message = mHandler.obtainMessage();
                Bundle bundle = new Bundle();
                bundle.putString("isSuccess", isSuccess);
                bundle.putInt("userID", id);
                message.setData(bundle);
                mHandler.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}
}

package com.example.socketdemo.activity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.example.socketdemo.R;
import com.example.socketdemo.comm.Constans;
import com.example.socketdemo.comm.OnItemClickListener;
import com.example.socketdemo.domain.UserInfo;

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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Devin on 2016/8/1.
 */
public class FriendsActivity extends AppCompatActivity {
private RecyclerView rv_friends;
private FriendsAdapter mAdapter;
private List mUserInfos;
private int userID;
private FriendHandler mHandler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_friends);
    userID = getIntent().getIntExtra("userID", 0);
    mHandler = new FriendHandler();
    rv_friends = (RecyclerView) findViewById(R.id.rv_friends);
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    rv_friends.setLayoutManager(linearLayoutManager);
    rv_friends.setItemAnimator(new DefaultItemAnimator());
    mUserInfos = new ArrayList<>();
    initData(userID);
    mAdapter = new FriendsAdapter(this, mUserInfos);
    rv_friends.setAdapter(mAdapter);
    mAdapter.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(View view, int position) {
            UserInfo userInfo = mUserInfos.get(position);
            Intent intent = new Intent(FriendsActivity.this, ChatActivity.class);
            intent.putExtra("sendID", userID);
            intent.putExtra("receiveID", userInfo.getUserID());
            intent.putExtra("userName", userInfo.getUserName());
            startActivity(intent);
        }
    });
}

private void initData(final int userID) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Socket socket = new Socket();
            try {
                socket.connect(new InetSocketAddress(Constans.SERVER_ADDRESS, Constans.SERVER_PORT), 5000);

                PrintWriter writer = new PrintWriter(socket.getOutputStream());
                writer.println(Constans.TAG_FRIENDS + ":" + userID);
                writer.flush();

                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String result = reader.readLine();
                System.out.println("-----result---->>"+result);
                Bundle bundle = new Bundle();
                bundle.putString("result", result);
                Message message = mHandler.obtainMessage();
                message.setData(bundle);
                mHandler.sendMessage(message);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

private class FriendHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Bundle bundle = msg.getData();
        String result = bundle.getString("result");
        List infos = new ArrayList<>();
        try {
            JSONArray jsonArray = new JSONArray(result);
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                UserInfo userInfo = new UserInfo();
                userInfo.setUserID(jsonObject.getInt("userID"));
                userInfo.setUserIcon(jsonObject.getInt("userIcon"));
                userInfo.setUserName(jsonObject.getString("userName"));
                userInfo.setUserSign(jsonObject.getString("userSign"));
                userInfo.setUserState(jsonObject.getInt("userState"));
                infos.add(userInfo);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        mAdapter.addList(infos);
    }
}
}

實現效果圖

\

這樣可以實現簡單的Socket通信,當然只是很簡單的應用。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved