Android网络编程之TCP/UDP学习
本文部分资料来源于网络,仅作为学习纪录用途。
Andorid网络编程简介:
通过网络实现客户端与服务端数据的共享访问
网络通讯模型
开放系统互连参考模型:
- 应用层:通讯的应用程序(类似快递客户端)
- 表示层:数据的表示格式(类似快递的种类)
- 会话层:开始、控制和结束一个会话(类似快递点接揽业务)
- 传输层:网络传输的通讯规则(类似快递运输的方式和规则)
- 网络层:识别目标机器的IP地址(类似快递投放的目标地点)
- 数据链路层:单个链路传输数据规则,主要由运营商来定义。(类似快递公司对于具体城市定义的规则)
- 物理层:传输介质的特性标准(类似快递运输的过程)
TCP/IP 模型 4 层:
- 应用层
- 传输层
- 网络层
- 主机至网络层
一个应用层应用一般都会使用到两个传输层协议之一:
面向连接的 TCP 传输控制协议和面向无连接的 UDP 用户数据报协议。
下面分析 TCP/IP 协议栈中常用的 IP、 TCP 和 UDP 协议。
- TPC面向连接,具有可靠性,流控制,错误恢复等特性。
- UDP面向无连接,比较简单,UDP头包含字节较少,所以比TCP负载消耗少。
UDP 用来支持那些需要在计算机之间传输数据的网络应用,包括网络视频会议系统在内的众多的客户端 / 服务器模式的网络应用都需要使用UDP协议。 UDP 协议的主要作用是将网络数据流量压缩成数据报的形式。
网络三要素
IP地址:32位二进制,划分为4段,每段范围0-255,采用十进制表示。
1. A类IP地址
一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”, 地址范围从1.0.0.0 到126.0.0.0。可用的A类网络有126个,每个网络能容纳1亿多个主机。2. B类IP地址
一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,地址范围从128.0.0.0到191.255.255.255。可用的B类网络有16382个,每个网络能容纳6万多个主机 。3. C类IP地址
一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”。范围从192.0.0.0到223.255.255.255。C类网络可达209万余个,每个网络能容纳254个主机。4. D,E类IP地址不常用,另外127.0.0.1是本机回环地址。
端口号
- 用于标识进程的逻辑地址,不同进程的标识
- 有效端口:0~65535,其中0-1024系统使用或保留端口
- 一般说的都是软件意义上的端口
- IP地址类似于门牌号,端口类似于房间号
传输协议
TCP
TCP(Transmission Control Protocol 传输控制协议
面向连接,基于字节流的传输层通信协议。
位于IP协议之上,可靠的端对端协议
重发技术,保证数据内容准确
大数据量传输
三次握手完成连接,属于可选协议
必须建立连接,效率稍低
类似于打电话UDP
用户数据报协议
无连接,速度快,不可靠的传输层协议
位于IP协议之上,即UDP数据报(64K内)是封装在IP数据包中进行传输
类似于发快递,发短信,常用于网络视频会议等情景IP
TCP/IP协议模型中关键部分,Internet中所有数据包都通过IP包的方式进行传输。
能够适应不同的网络硬件。
无连接
不可靠
InetAddress.java
* @see java.net.InetAddress#getByAddress(byte[])
* @see java.net.InetAddress#getByAddress(java.lang.String, byte[])
* @see java.net.InetAddress#getAllByName(java.lang.String)
* @see java.net.InetAddress#getByName(java.lang.String)
* @see java.net.InetAddress#getLocalHost()
Socket对象
- 套接字,为网络服务提供的一种机制
- 通信两端都是Socket
- 应用程序通过Socket向网络发送请求或响应网络请求
- 位于会话层
TCP工作流程
TCP 服务器端工作的主要步骤如下。
步骤 1 调用 ServerSocket( int port) 创建一个 ServerSocket, 并绑定到指定端口上。
步骤 2 调用 accept(), 监听连接请求, 如果客户端请求连接, 则接受连接,返回通信套接字。
步 骤 3 调 用 Socket 类 的 getOutputStream() 和 getInputStream() 获 取 输 出 和 输 入 流,
开始网络数据的发送和接收。
步骤 4 关闭通信套接字。
示例代码如下所示:
public class TcpReceiveDemo {
public static void main(String[] argv) throws IOException {
// 1. 创建serverSocket服务器端对象 监听具体的端口
ServerSocket ss = new ServerSocket(10002);
// 2. 获取具体的连接客户端对象
Socket s = ss.accept(); // 阻塞式
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + "--------connected---------");
// 3. 通过客户端对象获取读取流对象,读取客户端发送的消息
InputStream in = s.getInputStream();
byte[] buff = new byte[1024];
int len = in.read(buff);
String str = new String(buff, 0, len);
System.out.println("Client: " + str);
// 4. 获取socket对象,将数据写入客户端
OutputStream out = s.getOutputStream();
out.write("已收到".getBytes());
// 5. 关闭流对象
s.close();
ss.close();
}
}
TCP 客户端工作的主要步骤如下。
步骤 1 调用 Socket() 创建一个流套接字, 并连接到服务器端。
步骤 2 调用 Socket 类的 getOutputStream() 和 getInputStream() 方法获取输出和输入
流, 开始网络数据的发送和接收。
步骤 3 关闭通信套接字。
编写 TCP 客户端代码如下所示:
public class TcpSendDemo {
public static void main(String[] argv) throws IOException {
// 1. 创建客户端socket服务 指定目标地址及端口
Socket s = new Socket("192.168.0.103", 10002);
// 2. 通过socket对象向服务器写入数据
OutputStream out = s.getOutputStream();
out.write("Hello,服务器!!".getBytes());
// 2.1 通过socket对象获取服务器返回的数据
InputStream in = s.getInputStream();
byte[] buff = new byte[1024];
int len = in.read(buff);
String str = new String(buff, 0, len);
System.out.println("server: " + str);
// 3. 关闭资源
s.close();
}
}
测试流程同下面的UDP
UDP工作流程
UDP 服务器端工作的主要步骤如下。
步骤 1 调用 DatagramSocket(int port) 创建一个数据报套接字, 并绑定到指定端口上。
步骤 2 调用 DatagramPacket( byte[]buf,int length), 建立一个字节数组以接收 UDP 包。
步骤 3 调用 DatagramSocket 类的 receive(), 接受 UDP 包。
步骤 4 关闭数据报套接字。
public class UdpReceiveDemo {
public static void main(String[] argv) throws IOException {
// 建立udp socket服务
DatagramSocket socket = new DatagramSocket(9527);
while(true){
byte[] buff = new byte[1024];
// 准备用于存储发送数据的数据包
DatagramPacket dp = new DatagramPacket(buff, buff.length);
// 阻塞式的接收方法
socket.receive(dp);
String ip = dp.getAddress().getHostAddress();
String text = new String(dp.getData(), 0, dp.getLength());
System.out.println("ip:" + ip + "; 发送内容:" + text + "; 端口:" + dp.getPort());
}
// 由于这里一直在等待接收数据包,所以没有关闭资源
// socket.close();
}
}
UDP 客户端工作的主要步骤如下。
步骤 1 调用 DatagramSocket() 创建一个数据包套接字。
步骤 2 调用 DatagramPacket( byte[]buf,int offset,int length,InetAddress address,int port),
建立要发送的 UDP 包。
步骤 3 调用 DatagramSocket 类的 send() 发送 UDP 包。
步骤 4 关闭数据报套接字。
public class UdpSendDemo {
public static void main(String[] argv) throws IOException {
// 建立udp socket服务
DatagramSocket socket = new DatagramSocket(8888);
// 准备需要发送的数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine()) != null) {
byte[] buff = line.getBytes(); // 数据转换成字节数组
// 将需要发送的数据封装成数据包
DatagramPacket dp = new DatagramPacket(buff, buff.length, InetAddress.getByName("192.168.0.103"),9527);
// 调用send方法将数据包发送
socket.send(dp);
if ("886".equals(line)) {
break;
}
}
// 关闭资源
socket.close();
}
}
- 发送端端口8888和接收端端口9527都是自定义的
- 192.168.0.103是接收端的地址,由于在本地主机上做实验,所以用的是本机IP
BufferedReader br = new BufferedReader(new InputStreamReader(System.in))
将标准输入流转换成BufferedReader。
联合测试查看实验现象:
1. 将发送和接收端两个java文件单独拷贝出来,并去掉文件中的包名
2. 使用javac分别编译,并在两个终端中使用java运行。
3. 在运行发送端程序的终端输入字符,并在运行接收端程序的终端进行查看
实验现象: