整个编码过程在Android studio 3.6.1中进行的,不要忘记申请网络权限哦,全篇文章都是聊天室为例

Android中常用的网络通信有http,https,socket,websocket,其中http和https是通信协议。socket和websocket是基于tcp/udp协议的编程接口。

一、相关知识

网络体系结构

网络体系结构有三种,OSI体系结构、TCP / IP体系结构、五层体系结构

  • OSI体系结构:概念清楚 & 理念完整,但复杂 & 不实用。
  • TCP / IP体系结构:包含了一系列构成互联网基础的网络协议,是Internet的核心协议被广泛应用于局域网和广域网。
  • 五层体系结构:融合了OSI 与 TCP / IP的体系结构,目的是为了学习和计算机原理。

TCP/IP 是个协议族,是互联网的核心,可以分四个层次:链路层,网络层,传输层和应用层。

每个层对应的协议以及功能在表中已给出。

网络传输的过程

如何将一个数据从一个终端传到另一个终端的呢?下面是数据流转的过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMzVV2K8-1587015624687)(en-resource://database/4214:1)]
每次往下层传输都需要将本层的信息带上,并放在头部中,发送端是数据封装的过程,接收端是信息解封装的过程。

在传输过程中,涉及几个概念
IP:用于找到网络中的主机和路由器
MAC:用于区分同一链路中不同的计算机。
Port:用于识别同一个计算机中的不同程序,相当于程序在网络的地址。端口范围0-65535,其中0-1023为系统端口。下面列举一些系统默认的端口号。

协议名称 协议功能 默认端口号
HTTP(HypertextTransfer Protocol)超文本传输协 议浏览网页 80
FTP(File TransferProtocol) 文件传输协议 用于网络上传输文件 21
TELNET 远程终端访问 23
POP3(Post OfficeProtocol) 邮局协议版本 110
  • TCP/UDP 对比
UDP TCP
是否连接 无连接 面向连接
是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制
连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信
传输方式 面向报文 面向字节流
首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节
适用场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,例如文件传输

在Android中socket,websocket,tcp,udp

二、Socket使用

Socket不是通信协议,是一种基于TCP/UDP协议的编程接口。

1.采用TCP方式

若想实现两端的通信,两端必须需要使用Socket;客户端使用socket,服务端使用ServerSocket,两端约定好端口。socket端需要知道ServerSocket的tIP地址。

  • 1.建立连接
    客户端在建立连接时,需要在socket中约定好服务端IP地址和端口号(避免重复),如 Socket mSocket = new Socket(IP,Port);而服务端则只需要制定好端口,因为本机IP地址就是它的IP地址,如ServerSocket mServerSocket = new ServerSocket(8989);
  • 2.发送数据
    因为socket是采用C/S结构,所以建立好连接之后,就可以相互发数据了。
    mSocket.getOutputStream().write(msg); //获取socket中流,并将数据写入进去。仅支持byte[]这种格式。
    mSocket.getOutputStream().flush(); //刷新
    服务端和客户端本质上是一样的,只是服务端需要mSokcet = mServerSocket.accept() ,通过这个socket进行发送数据。
  • 3.接收数据
    要想接收来自其他端发来的数据,就必须获取流通道getInputStream();
    InputStream inputStream = mSocket.getInputStream(); ,不停地从里面取数据,然后根据发送端发过来的数据转成我们需要的数据。

搭建TCP Client

public class SocketUtil {         private Socket mSocket;    private ExecutorService mExecutorService;    public SocketUtil() {             mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());    }    public void startClient(final String address, final int port) {             if (address == null) {                 return;        }        if (mSocket == null) {                 Runnable mRunnable = new Runnable() {                     @Override                public void run() {                         try {                             Log.i("tcp", "启动客户端");                        mSocket = new Socket(address, port);                        Log.i("tcp", "客户端连接成功");                        InputStream inputStream = mSocket.getInputStream();                        byte[] buffer = new byte[1024];                        int len = -1;                        while ((len = inputStream.read(buffer)) != -1) {                                 String data = new String(buffer, 0, len);                            mIMessageCallback.callback(data);                            Log.i("tcp", "收到服务器的数据-------------------:" + data);                        }                        Log.i("tcp", "客户端断开连接");                    } catch (Exception EE) {                             EE.printStackTrace();                        Log.i("tcp", "客户端无法连接服务器" + EE.getMessage());                    } finally {                             try {                                 if (mSocket != null) {                                     mSocket.close();                            }                        } catch (IOException e) {                                 e.printStackTrace();                        }                        mSocket = null;                    }                }            };            mExecutorService.execute(mRunnable);        }    }    public void sendTcpMessage(final byte[] msg) {             if (mSocket != null && mSocket.isConnected()) {                 Runnable mRunnable = new Runnable() {                     @Override                public void run() {                         try {                             mSocket.getOutputStream().write(msg);                        mSocket.getOutputStream().flush();                    } catch (IOException e) {                             e.printStackTrace();                    }                }            };            mExecutorService.execute(mRunnable);        }    }    IMessageCallback mIMessageCallback;    public interface IMessageCallback {             void callback(String string);    }    public void setIMessageCallback(IMessageCallback messageCallback) {             this.mIMessageCallback = messageCallback;    }}

搭建TCP Server

public class SocketUtil {         private ServerSocket mServerSocket;    private ExecutorService mExecutorService;    private Socket mSocket;    public SocketUtil() {             mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());        try {                 Log.i("tcp", "启动服务端");            mServerSocket = new ServerSocket(8989);            Log.i("tcp", "服务端连接成功");        } catch (Exception e) {                 Log.i("tcp", "启动服务端失败");        }    }    public void startSever() {             Runnable mRunnable = new Runnable() {                 @Override            public void run() {                     while (true) {                         try {                             mSocket = mServerSocket.accept();                        Log.i("tcp", mSocket.getRemoteSocketAddress().toString() + "");                        InputStream inputStream = mSocket.getInputStream();                        byte[] buffer = new byte[1024];                        int len = -1;                        while ((len = inputStream.read(buffer)) != -1) {                                 String data = new String(buffer, 0, len);                            mIMessageCallback.callback(data);                            Log.i("tcp", "收到客户端的数据-------------------:" + data);                        }                        Log.i("tcp", "客户端断开连接");                    } catch (Exception EE) {                             EE.printStackTrace();                        Log.i("tcp", "服务端无法连接服务器" + EE.getMessage());                        break;                    } finally {                             try {                                 if (mServerSocket != null) {                                     mServerSocket.close();                            }                        } catch (IOException e) {                                 e.printStackTrace();                        }                        mServerSocket = null;                    }                }            }        };        mExecutorService.execute(mRunnable);    }    public void sendTcpMessage(final byte[] msg) {             Runnable mRunnable = new Runnable() {                 @Override            public void run() {                     try {                         mSocket.getOutputStream().write(msg);                    mSocket.getOutputStream().flush();                } catch (IOException e) {                         e.printStackTrace();                }            }        };        mExecutorService.execute(mRunnable);    }    public void des() {             try {                 mServerSocket.close();        } catch (Exception e) {             }    }    public IMessageCallback mIMessageCallback;    public interface IMessageCallback {             void callback(String string);    }    public void setIMessageCallback(IMessageCallback messageCallback) {             this.mIMessageCallback = messageCallback;    }}

2.采用UDP方式

UDP其实没有服务端和客户端的概念,但是和TCP用法也是类似,在这里需要注意两个概念,DatagramSocketDatagramPacket。UDP是面向数据包的传输层协议,所以在接收和发送都是以数据包的形式。

IP数据报的最大长度为 65535 字节 ,除去首字IP 的20 字节和 UDP首部8个字节,实际上,UDP 能传输的最大字节数为 65507,个字节;当我们的数据超过这个长度时,则需要考虑分包的问题,

  • 1.接收数据
    在接收UDP数据时,需要通过DatagramSocket监听指定端口,IP地址就是当前主机的IP地址,接收的数据格式为DatagramPacket。

  • 2.发送数据
    接收数据采用的数据为DatagramPacket,发送数据也是如此,只是在发送数据时需要指定IP和端口,因为它不是面向连接的,发送过去至于能不能接收,它是不知道的再不关注,只需要将数据发送指定的端口和IP就行了。高效率就体现在如此,没有tcp三次握手和四次挥手的过程。

搭建UDP Client

public class SocketUDPClientUtil {         private DatagramSocket mDatagramSocket;    private ExecutorService mExecutorService;    private DatagramPacket sendPacket;    private DatagramPacket receivePacket;    public SocketUDPClientUtil() {             mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());    }    public void startClient() {             if (mDatagramSocket == null) {                 Runnable mRunnable = new Runnable() {                     @Override                public void run() {                         try {                             Log.i("udp", "启动客户端");                        mDatagramSocket = new DatagramSocket(8989);//本机监听的端口                        mDatagramSocket.setReuseAddress(true);                        Log.i("udp", "客户端连接成功");                        byte[] buffer = new byte[1024];                        int len = -1;                        while (true) {                                 receivePacket = new DatagramPacket(buffer, buffer.length);                            mDatagramSocket.receive(receivePacket);                            Log.i("udp", receivePacket.getAddress().toString() + receivePacket.getPort());                            String data = new String(receivePacket.getData(), 0, receivePacket.getData().length, "utf-8");//针对中文要采用utf-8                            mIMessageCallback.callback(data);                            Log.i("udp", "收到服务器的数据-------------------:" + data);                        }//                        Log.i("udp", "客户端断开连接");                    } catch (Exception EE) {                             EE.printStackTrace();                        Log.i("udp", "客户端无法连接服务器" + EE.getMessage());                    } finally {                             try {                                 if (mDatagramSocket != null) {                                     mDatagramSocket.close();                            }                        } catch (Exception e) {                                 e.printStackTrace();                        }                        mDatagramSocket = null;                    }                }            };            mExecutorService.execute(mRunnable);        }    }    /**     * @param msg  要发送的数据     * @param ip   目标IP地址     * @param port 目标端口号     */    public void sendTcpMessage(final byte[] msg, final String ip, final int port) {             if (mDatagramSocket != null) {                 Runnable mRunnable = new Runnable() {                     @Override                public void run() {                         try {                             sendPacket = new DatagramPacket(msg, msg.length, InetAddress.getByName(ip), port);                        mDatagramSocket.send(sendPacket);                    } catch (IOException e) {                             e.printStackTrace();                    }                }            };            mExecutorService.execute(mRunnable);        }    }    IMessageCallback mIMessageCallback;    public interface IMessageCallback {             void callback(String string);    }    public void setIMessageCallback(IMessageCallback messageCallback) {             this.mIMessageCallback = messageCallback;    }}

搭建UDP Server

public class SocketServerUtil {         private DatagramSocket mDatagramSocket;    private ExecutorService mExecutorService;    private DatagramPacket sendPacket;    private DatagramPacket receivePacket;    public SocketUtil() {             mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());        try {                 Log.i("udp", "启动服务端");            mDatagramSocket = new DatagramSocket(8989);            Log.i("udp", "服务端连接成功");        } catch (Exception e) {                 Log.i("udp", "启动服务端失败");        }    }    public void startClient() {             Runnable mRunnable = new Runnable() {                 @Override            public void run() {                     try {                         byte[] buffer = new byte[1024];                    int len = -1;                    receivePacket = new DatagramPacket(buffer, buffer.length);                    while (true) {                             mDatagramSocket.receive(receivePacket);                        String data = new String(receivePacket.getData(), 0, buffer.length, "utf-8");//针对中文要采用utf-8                        mIMessageCallback.callback(data);                        Log.i("udp", "收到服务器的数据-------------------:" + data);                    }                } catch (Exception EE) {                         EE.printStackTrace();                    Log.i("udp", "客户端无法连接服务器" + EE.getMessage());                } finally {                         try {                             if (mDatagramSocket != null) {                                 mDatagramSocket.close();                        }                    } catch (Exception e) {                             e.printStackTrace();                    }                    mDatagramSocket = null;                }            }        };        mExecutorService.execute(mRunnable);    }    public void sendTcpMessage(final byte[] msg, final String ip, final int port) {             if (mDatagramSocket != null) {                 Runnable mRunnable = new Runnable() {                     @Override                public void run() {                         try {                             sendPacket = new DatagramPacket(msg, msg.length, InetAddress.getByName(ip), port);                        mDatagramSocket.send(sendPacket);                    } catch (IOException e) {                             e.printStackTrace();                    }                }            };            mExecutorService.execute(mRunnable);        }    }    public void des() {             try {                 mDatagramSocket.close();        } catch (Exception e) {                 Log.i(TAG, e.toString());        }    }    public IMessageCallback mIMessageCallback;    public interface IMessageCallback {             void callback(String string);    }    public void setIMessageCallback(IMessageCallback messageCallback) {             this.mIMessageCallback = messageCallback;    }

两端代码一样的,TCP还分Socket和ServerSocket,而UDP完全没有

单播,组播和广播实现

由于UDP是面向无连接的,所有才有组播和广播的概念。

  • 1.单播
    单播其实上面已经实现了,就是IP地址是确定的,实现一对一发送数据。
  • 2.广播
    一对所有,所有指的是可能到达网络节点,一般就是局域网中的所有节点。主要用于音视频分发,但是在公共网络中并没有用这个技术,因为容易造成网络中不必要的数据以及隐私问题。

发送时将目标地址设置为:255.255.255.255 即可实现广播功能,代码就不贴出来了。

广播自己也能收到,所以若是不需要可以根据IP地址进行过滤掉。

  • 3.组播
    组播介于单播和广播之间,即一对指定网络节点
    多播的地址是特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类:  
    1、局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。
    2、预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议。
    3、管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。

组播实现
组播的实现与单播和广播类似,可能在对象上不同,采用MulticastSocket,然后加入到组播地址中,凡是加入到这个地址的都可以收到数据。

MulticastSocket mDatagramSocket = new MulticastSocket(8989);//本机监听的端口mDatagramSocket.joinGroup(InetAddress.getByName("225.0.0.1"));

三、Websocket使用

WebSocket是为HTML5提供的一种在单个TCP连接上进行全双工通讯的编程接口。

添加依赖:

implementation “org.java-websocket:Java-WebSocket:1.4.0”

  • 1.地址形式
    它与之前的socket不同,地址的形式为“ws://ip:port"

  • 2.发送信息
    服务端发送数据也是通过socket的,在建立连接时需要保持这个socket而已。见服务端代码。

  • 3.接受数据

在onMessage中接收数据,但是在代码可以看出,它接收其实String,要是你想接收流,必须先转成string才行。

搭建Websocket Client

public class MyWebSocket extends WebSocketClient {         public MyWebSocket(URI serverUri) {             super(serverUri);        setReuseAddr(true);    }    @Override    public void onOpen(ServerHandshake handshakedata) {             Log.e("haochen", "message " + handshakedata.toString());    }    @Override    public void onMessage(String message) {             Log.e("haochen", "message" + message);        mIMessageCallback.callback(message);    }    @Override    public void onClose(int code, String reason, boolean remote) {             Log.e("haochen", "message" + reason);    }    @Override    public void onError(Exception ex) {             Log.e("haochen", "message" + ex.toString());    }    IMessageCallback mIMessageCallback;    public interface IMessageCallback {             void callback(String string);    }    public void setIMessageCallback(IMessageCallback messageCallback) {             this.mIMessageCallback = messageCallback;    }}

搭建WebSocket Server

public class MyWebSocketServer extends WebSocketServer {         public WebSocket mWebSocket;    public final static String TAG = MyWebSocketServer.class.getSimpleName();    public MyWebSocketServer(InetSocketAddress address) {             super(address);        setReuseAddr(true);    }    @Override    public void onOpen(WebSocket conn, ClientHandshake handshake) {             mWebSocket = conn;        conn.send("server is opening" + conn.getRemoteSocketAddress());        Log.e(TAG, "open");    }    @Override    public void onClose(WebSocket conn, int code, String reason, boolean remote) {             conn.close();        Log.e(TAG, "close" + reason);    }    @Override    public void onMessage(WebSocket conn, String message) {             mWebSocket = conn;        mIMessageCallback.callback(message);        Log.e(TAG, "message" + message + conn.getRemoteSocketAddress());//        conn.send("iflytek123" + message);    }    @Override    public void onError(WebSocket conn, Exception ex) {             if (conn != null) {                 conn.closeConnection(1, "close");            Log.e(TAG, "ex " + ex.toString() + conn.getRemoteSocketAddress());        } else {                 Log.e(TAG, "cnn is null " + ex.getMessage());        }    }    @Override    public void onStart() {             Log.e(TAG, "onStart");    }    public WebSocket getWebSocket() {             return mWebSocket;    }    public void clearConnection() {             try {                 stop();        } catch (Exception e) {             }    }    IMessageCallback mIMessageCallback;    public interface IMessageCallback {             void callback(String string);    }    public void setIMessageCallback(IMessageCallback messageCallback) {             this.mIMessageCallback = messageCallback;    }}

websocket 主要解决H5与其他端的通信,若是有一端是H5,又想通过TCP实现通信,这时候你可以选websocket。

坑:需要设置setReuseAddr(true),不然你退出在进入的话,需要等2-4分钟才能绑定同一个地址。

参考

Android Socket使用详解
太厉害了,终于有人能把TCP/IP 协议讲的明明白白了
安卓网络知识总结(一)–网络基础知识
Android 你不得不学的HTTP相关知识
A Practical Guide to Differentiate Unicast, Broadcast & Multicast
Java UDP 单播、多播(组播)、广播、任播(未实现)

更多相关文章

  1. GreenDao 3.0 简介、使用及踩坑
  2. 一文详解Android(安卓)轻量级存储方案的前世今生
  3. Android(安卓)Camera数据流分析全程记录
  4. 【Android(安卓)开发】:UI控件之 ListView 列表控件的使用
  5. android 音乐视频播放器(github上十二款最著名的Android播放器开
  6. Android多个APK共享数据(Shared User ID)
  7. android访问远程数据库
  8. Android基础类之BaseAdapter
  9. mybatisplus的坑 insert标签insert into select无参数问题的解决

随机推荐

  1. 【ubuntu】Ubuntu中Android(安卓)SDK下载
  2. Android视频采集
  3. 实例分析android中的Binder通信机制(1)
  4. 如何在Android中使用OpenCV
  5. 【转】不要被虚张声势的 Android 忽悠了
  6. Android 读取硬件信息技巧
  7. windows 8环境—android studio初步体验(
  8. Android彻底组件化方案实践
  9. 王家林,云计算,大数据,Hadoop,Android,iOS,HTML
  10. 安卓View理解总结