Socket通信初探

ZERO、前言

有關Socket通信原理內容是在網上或百科整理得到,代碼部分爲本人所寫,如果不當,還望指教。



一、Socket通信簡介 

Android與服務器的通信方式主要有兩種,一是Http通信,一是Socket通信。兩者的最大差異在於,http連接使用的是“請求—響應方式”,即在請求時建立連接通道,當客戶端向服務器發送請求後,服務器端才能向客戶端返回數據。而Socket通信則是在雙方建立起連接後就可以直接進行數據的傳輸,在連接時可實現信息的主動推送,而不需要每次由客戶端想服務器發送請求。 那麼,什麼是socket?Socket又稱套接字,在程序內部提供了與外界通信的端口,即端口通信。通過建立socket連接,可爲通信雙方的數據傳輸傳提供通道。socket的主要特點有數據丟失率低,使用簡單且易於移植。


1.1什麼是Socket Socket
是一種抽象層,應用程序通過它來發送和接收數據,使用Socket可以將應用程序添加到網絡中,與處於同一網絡中的其他應用程序進行通信。簡單來說,Socket提供了程序內部與外界通信的端口併爲通信雙方的提供了數據傳輸通道。


 1.2Socket的分類
 根據不同的的底層協議,Socket的實現是多樣化的。本指南中只介紹TCP/IP協議族的內容,在這個協議族當中主要的Socket類型爲流套接字(streamsocket)和數據報套接字(datagramsocket)。流套接字將TCP作爲其端對端協議,提供了一個可信賴的字節流服務。數據報套接字使用UDP協議,提供數據打包發送服務。 下面,我們來認識一下這兩種Socket類型的基本實現模型。


二、Socket 基本通信模型



三、Socket基本實現原理


 3.1基於TCP協議的Socket 
服務器端首先聲明一個ServerSocket對象並且指定端口號,然後調用Serversocket的accept()方法接收客戶端的數據。accept()方法在沒有數據進行接收的處於堵塞狀態。(Socketsocket=serversocket.accept()),一旦接收到數據,通過inputstream讀取接收的數據。
  客戶端創建一個Socket對象,指定服務器端的ip地址和端口號(Socketsocket=newSocket("172.168.10.108",8080);),通過inputstream讀取數據,獲取服務器發出的數據(OutputStreamoutputstream=socket.getOutputStream()),最後將要發送的數據寫入到outputstream即可進行TCP協議的socket數據傳輸。
3.2基於UDP協議的數據傳輸 
服務器端首先創建一個DatagramSocket對象,並且指點監聽的端口。接下來創建一個空的DatagramSocket對象用於接收數據(bytedata[]=newbyte[1024;]DatagramSocketpacket=newDatagramSocket(data,data.length)),使用DatagramSocket的receive方法接收客戶端發送的數據,receive()與serversocket的accepet()類似,在沒有數據進行接收的處於堵塞狀態。
客戶端也創建個DatagramSocket對象,並且指點監聽的端口。接下來創建一個InetAddress對象,這個對象類似與一個網絡的發送地址(InetAddressserveraddress=InetAddress.getByName("172.168.1.120")).定義要發送的一個字符串,創建一個DatagramPacket對象,並制定要講這個數據報包發送到網絡的那個地址以及端口號,最後使用DatagramSocket的對象的send()發送數據。*(Stringstr="hello";bytedata[]=str.getByte();DatagramPacketpacket=new DatagramPacket(data,data.length,serveraddress,4567);socket.send(packet);)

代碼如下:

客戶端:

package com.example.socketdemo;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

public class ClientTCPSocket {
    private Context context;
    private Handler handler;

    public ClientTCPSocket(Context context, Handler handler) {
        this.context = context;
        this.handler = handler;

    }

    public void client() {
        Socket socket;
        try {
            // 創建一個Socket對象,並指定服務端的IP及端口號,這裏服務端的IP就是手機的Ip
            socket = new Socket(GetHostIp(), 1989);
            // 從手機選擇一個文件讀取
            InputStream inputStream = new FileInputStream("/mnt/sdcard/a.txt");
            // 獲取Socket的OutputStream對象用於發送數據。
            OutputStream outputStream = socket.getOutputStream();
            // 創建一個byte類型的buffer字節數組,用於存放讀取的本地文件
            byte buffer[] = new byte[4 * 1024];
            int temp = 0;
            while ((temp = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, temp);
            }
            InputStream socketStream = socket.getInputStream();
            byte buf[] = new byte[1024];
            int tem = 0;
            // 從InputStream當中讀取客戶端所發送的數據 ,發送一個Handler用於更新界面
            while ((tem = socketStream.read(buf)) != -1) {
                Message msg = new Message();
                msg.what = 1;
                msg.obj = new String(buf, 0, tem, "UTF-8");
                handler.sendMessage(msg);
            }
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 獲得手機的IP地址
    public static String GetHostIp() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en
                    .hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> ipAddr = intf.getInetAddresses(); ipAddr
                        .hasMoreElements();) {
                    InetAddress inetAddress = ipAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        return inetAddress.getHostAddress();
                    }
                }
            }
        } catch (SocketException ex) {
        } catch (Exception e) {
        }
        return null;
    }
}

服務端:

package com.example.socketdemo;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

public class ServerTCPSocket {
    private Context context;
    private Handler handler;

    public ServerTCPSocket(Context context, Handler handler) {
        this.context = context;
        this.handler = handler;
    }

    public void server() {
        // 聲明一個ServerSocket對象
        ServerSocket serverStock = null;
        try {
            // 創建一個ServerSocket對象,並讓這個Socket在1989端口監聽
            serverStock = new ServerSocket(1989);
            // 調用ServerSocket的accept()方法,該方法是一個阻塞方法,接受客戶端所發送的請求,
            // 如果客戶端沒有發送數據,那麼該線程就停滯不繼續
            Socket socket = serverStock.accept();

            // 從Socket當中得到OutputStream對象,用於向客戶端發送數據
            String out = "服務器已收到";
            byte buf[] = out.getBytes();
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(buf, 0, buf.length);

            // 從Socket當中得到InputStream對象
            InputStream inputStream = socket.getInputStream();
            byte buffer[] = new byte[1024 * 4];
            int temp = 0;
            // 從InputStream當中讀取客戶端所發送的數據 ,發送一個Handler用於更新界面
            while ((temp = inputStream.read(buffer)) != -1) {
                Message msg = new Message();
                msg.what = 0;
                msg.obj = new String(buffer, 0, temp, "gb2312");
                handler.sendMessage(msg);
            }
            serverStock.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

主文件:

package com.example.socketdemo;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    private Button sendBtn, receiveBtn;
    private TextView receiveTxt, sendTxt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sendBtn = (Button) findViewById(R.id.btn_send);
        receiveBtn = (Button) findViewById(R.id.btn_receive);
        sendTxt = (TextView) findViewById(R.id.txt_send);
        receiveTxt = (TextView) findViewById(R.id.txt_receive);
        sendBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {

                new Thread() {
                    public void run() {
                        ClientTCPSocket socket = new ClientTCPSocket(MainActivity.this, handler);
                        socket.client();
                    };
                }.start();

            }
        });
        receiveBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                new Thread() {
                    public void run() {
                        ServerTCPSocket socket = new ServerTCPSocket(MainActivity.this, handler);
                        socket.server();
                    };
                }.start();

            }
        });
    }

    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            if (msg.what == 0)
                receiveTxt.setText((String) msg.obj);
            else if (msg.what == 1) {
                sendTxt.setText((String) msg.obj);
            }
        };
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

記得在AndroidManifest.xml里加入權限

  <!-- 允許應用程序改變網絡狀態 -->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

    <!-- 允許應用程序改變WIFI連接狀態 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <!-- 允許應用程序訪問有關的網絡信息 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- 允許應用程序訪問WIFI網卡的網絡信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <!-- 允許應用程序完全使用網絡 -->
    <uses-permission android:name="android.permission.INTERNET" />



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章