Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中使用Socket

Android中使用Socket

編輯:關於Android編程

Socket簡介

在說Socket之前,我們有必要先來簡單介紹一下TCP/IP協議族,TCP/IP(Transmission Control Protocol/Internet Protocol)即 傳輸控制協議/網間協議,定義了主機如何連入因特網及數據如何再它們之間傳輸的標准,

從字面意思來看TCP/IP是TCP和IP協議的合稱,但實際上TCP/IP協議是指因特網整個TCP/IP協議族。不同於ISO模型的七個分層,TCP/IP協議參考模型把所有的TCP/IP系列協議歸類到四個抽象層中

應用層:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等

傳輸層:TCP,UDP

網絡層:IP,ICMP,OSPF,EIGRP,IGMP

數據鏈路層:SLIP,CSLIP,PPP,MTU

每一抽象層建立在低一層提供的服務上,並且為高一層提供服務,看起來大概是這樣子的
TCP/IP 示意圖

注意看我們的傳輸層,它大致有2個協議,一個是TCP 一個是UDP,他們就與我們的Socket息息相關了.
什麼是socket呢?我們經常把socket翻譯為套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層復雜的操作抽象為幾個簡單的接口供應用層調用已實現進程在網絡中通信。 如下圖
pasted-image
也就是說,我們可以通過直接操作Socket來實現使用TCP或者UDP來發送數據

使用TCP協議來傳輸數據

TCP協議 是一種面向連接的、可靠的、基於字節流的傳輸層通信協議,關於它為什麼穩定,3次握手和4次揮手的細節就不贅述了,可以自行Google,我們需要知道的是,在使用基於TCP協議的Socket的時候,我們操作的對象是流就可以了

代碼

通過一個小例子,我們來看一下在Android中使用的基於TCP協議的Socket通信

服務端

我們在進行Socket通信的時候,是需要兩個部分的,一個是服務端,一個是客戶端,我們用一段Java代碼來模擬服務端,用Android程序來模擬客戶端,但是值得注意的是,我們也是可以將Android程序作為服務端的

WebConfig



    

可以看到布局文件並不是很復雜,就是一個線性布局,裡面一個TextView,一個Button
然後看看我們的MainActivity

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.HorizontalScrollView;

import org.json.JSONArray;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {
    private Button sendBtn;
    private EditText mainEt;
    private String host  = "192.168.31.228";
    private int port = 8088;
    private Socket client;

    private PrintStream out;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sendBtn = (Button) findViewById(R.id.send_btn);
        mainEt = (EditText) findViewById(R.id.main_et);

        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                    String str = mainEt.getText().toString();
                    //發送數據到服務端
                    out.println(str);
                    if("bye".equals(str)){
                        out.close();
                        try {
                            client.close();
                            client = null;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
            }
        });

        initSocket();
    }

     private void initSocket() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    client = new Socket(host, port);
                    client.setKeepAlive(true);
                    //獲取Socket的輸出流,用來發送數據到服務端
                    out = new PrintStream(client.getOutputStream());
                    InputStream inputStream = client.getInputStream();
                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                    String s = "";
                    while ((s = bufferedReader.readLine()) != null) {
                        Log.d("MainActivity", s);
                    }
                    bufferedReader.close();
                    inputStreamReader.close();
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

代碼也並不是很復雜,在onCreate方法裡 我們調用initSocket()方法,值得注意的是,使用Socket來進行通信的時候,Socket創建的代碼一定要放到子線程中的.然後拿到socket的輸出流,並設置為一個全局變量,然後獲得輸入流,在這個輸入流裡,循環的讀取遠程傳過來的信息,直到對面的流關閉了,而在點擊事件裡,每一次點擊,都獲取EditText中的內容,通過輸出流 發送到遠端,注意host的地址,必須要填寫服務端的地址,由於我是服務端和手機端都是在同一個局域網裡的,所以這裡就填局域網的ip就可以了.
最後還需要在清單文件裡聲明網絡權限

使用UDP的Socket通信

UDP與TCP的區別在於 UDP並不是一個面向連接的,也就是我們沒辦法獲得輸入輸出流,而UDP也不是一個可靠的通信,我們沒辦法保證UDP發送的數據一定會傳到,中間不會丟包,丟了我們也不知道,他就是發,不管對面能不能接到,也沒有客戶端和服務端的概念,但是UDP的優勢在於快,直接就發,不需要握手,不需要回執,發就可以了,所以在網絡不穩定的時候,或者局域網,即時通訊這些場景下,UDP還是比較常見的.

TCP UDP 是否面向連接 面向連接 是否可靠 可靠的 應用場景 傳輸大量數據 速度 慢

代碼

由於也需要2個部分才能進行數據的傳遞,所有我們這裡同樣,也是一個使用電腦上的代碼,一個運行在Android虛擬機裡

電腦端

同樣 我們先來寫一個電腦端的代碼

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * Created by ChenFengYao on 16/9/30.
 */
public class Main {
    public static void main(String[] args) throws IOException {
        String strSend = "Hello ANDROID";
        byte[] buf = new byte[1024];
        //服務端在3000端口監聽接收到的數據
        DatagramSocket ds = new DatagramSocket(3000);
        //接收從客戶端發送過來的數據
        DatagramPacket dpReceive = new DatagramPacket(buf, 1024);
        System.out.println("server is on,waiting for client to send data......");
        boolean f = true;
        while (f) {
            //服務器端接收來自客戶端的數據
            ds.receive(dpReceive);
            System.out.println("server received data from client:");
            String strReceive = new String(dpReceive.getData(), 0, dpReceive.getLength()) +
                    " from " + dpReceive.getAddress().getHostAddress() + ":" + dpReceive.getPort();
            System.out.println(strReceive);
            //數據發動到客戶端的3000端口
            DatagramPacket dpSend = new DatagramPacket(strSend.getBytes(), strSend.length(), dpReceive.getAddress(), 3000);
            ds.send(dpSend);
            //由於dp_receive在接收了數據之後,其內部消息長度值會變為實際接收的消息的字節數,
            //所以這裡要將dp_receive的內部消息長度重新置為1024
            dpReceive.setLength(1024);
        }
        ds.close();
    }
}

注釋寫的還是比較詳細的,可以看到,在使用UDP的時候,我們發送和接收使用的都是DatagramSocket,而無論是發送 還是接收的數據,都是放到DatagramPacket裡面的,而數據是要放進byte數組裡的,這裡發送和接收的端口號是可以不同的,因為雙方都可以看成是服務器,所有都需要指定端口

Android

再看看Android端,Android端的代碼實際上和電腦端的是非常類似的

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * Created by ChenFengYao on 16/7/26.
 */
public class UDPAty extends AppCompatActivity {
    private Button sendBtn;
    private EditText mainEt;
    private String mHost = "192.168.43.162";
    private int port = 3000;

    private DatagramSocket mDatagramSocket;
    private InetSocketAddress mSocketAddress;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initUDP();

        sendBtn = (Button) findViewById(R.id.send_btn);
        mainEt = (EditText) findViewById(R.id.main_et);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String recData = mainEt.getText().toString();
                        //發送數據到
                        try {
                            DatagramPacket dpSend = new DatagramPacket(recData.getBytes()
                                    , recData.getBytes().length
                                    , mSocketAddress);
                            mDatagramSocket.send(dpSend);

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


            }
        });
    }

    private void initUDP() {
        try {
            mDatagramSocket = new DatagramSocket(port);
            //每個數據包的大小
            new Thread(new Runnable() {

                private String mData;

                @Override
                public void run() {
                    while (true) {
                        mSocketAddress = new InetSocketAddress(mHost,port);
                        try {
                            //一直等待接收數據
                            byte[] buffer = new byte[1024];
                            DatagramPacket receiver = new DatagramPacket(buffer, 1024);
                            mDatagramSocket.receive(receiver);
                            mData = new String(receiver.getData(),0,receiver.getLength());
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Toast.makeText(UDPAty.this, mData, Toast.LENGTH_SHORT).show();
                                }
                            });
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();

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

可以看到,同樣的 也是需要DatagramSocket 來發送和接收數據的,所以說 對於UDP來說,是不分服務端和客戶端的

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