一.网络分层
这段时间准备整理一下关于网络编程的一些知识点,准备整理的内容包括,TCP/UDP方向、Socket方向、HttpURLConnection与HttpClient方向、Http与Https方向,有时间的话再分析一个Android三方源码关于网络的封装,如Okhttp或者Volley或者Retrofit其中之一吧,那么今天我们就来看一下第一部分TCP/UDP相关内容,我们先从网络的分层说起,网络整体设计是一个很宽泛很复杂的问题,为了解决这个问题,很多网络采用分层的方式解决,其中比较常见的有两种:
1. 开放系统互连参考模型 (Open System Interconnect 简称OSI)是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合制定的开放系统互连参考模型,就是我们平时所说的7层模式,从低到高分别为:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
2. TCP/IP参考模型:分为4层,物理链路层、网络层,传输层,应用层。TCP/IP中的物理链路层对应OSI中的物理层和数据链路层 ,网络层对应网络层,传输层对应传输层,应用层对应会话层 、表示层、应用层。
二.基本概念
TCP(传输控制协议)、UDP(用户数据报协议)是位于传输层的协议。
TCP协议是一种安全性很高的协议,它能够保证数据安全准确的到达接收方,并保证数据顺序与发送时一致,TCP是面向连接的协议。每次建立连接都要经过3次握手:
1. 客户端发送一个SYN=j标志的TCP报文到服务器,进入等待状态。
2. 服务端接收到客户端的TCP报文后,会回复一个带ACK=j+1与SYN= k标志的报文,表示对1中报文的回复。
3. 当客户端接收到服务端的ACK+SYN包后,会回复一个ACK=K+1的包,发送完后,客户端与服务端进入ESTABLISHED状态,完成三次握手,这里需要注意,这些操作完成后客户端与服务器只是建立了连接,尚未真正传输数据,后续便可以传递我们想要传输的数据。数据传输完成后,客户端或者是服务器会发送断开连接的请求,不一定是服务器也不一定是客户端。断开连接需要4次握手,需要4次才能断开连接的原因是,当客户端或者服务器发起断开链接时,可能另一方还没有完成数据的操作,所以会多一步等待流程。
UDP是无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传送目的地,至于能够达到目的地,达到目的地的时间以及内容的正确性都不能保证。
TCP与UDP对比:
1. 在安全性能方面,TCP要略胜一筹,可靠的传输是要付出代价的,对数据内容的正确性的检验必然会占用计算机处理时间和网络带宽。通信过程中不容易出现数据丢失的现象,一方中断,两方的通信就会结束,UDP数据包传送的过程当中,一方中断,数据包有很大的可能性会丢失,还有可能传来的数据包的顺序是错乱的;
2. 在效率方面,UDP要比TCP快很多,最起码得知道他不用建立连接,至少节省了3次握手需要的时间。许多应用中并不需要保证严格的传输可靠性,比如视频会议系统,并不要求视频音频数据绝对正确,只要能够连贯就可以了。
三.TCP连接基本流程
1. 服务端创建ServerSocket,并绑定域名与端口。
2. 服务端调用accept()方法等待客户端调用。
3. 创建客户端Socket,指定服务器地址和端口。
4. 客户端通过输出流写入数据。
5. 服务端通过输入流读取客户端发送的数据。
6. 服务端通过输出流回复客户端。
7. 客户端通过输入流读取服务端的回复信息。
四.实现TCP连接实例。
1. 服务端代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author liuyonglei
* TCPServer端
*/
public class TCPServer {
public static void main(String[] args) {
try {
//1、创建服务器端ServerSocket,绑定端口,进行监听
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("LYL_服务器等待客户端连接");
//2、调用accept()方法,开始监听 等待客户端连接
Socket socket = serverSocket.accept();
//3、获取输入流读取客户端信息
InputStream is = socket.getInputStream();//字节输入流
InputStreamReader isr = new InputStreamReader(is);//将字节流转化为字符流
BufferedReader br = new BufferedReader(isr);//为输入流添加缓冲
String info=null;
while((info = br.readLine())!=null){//循环读取客户端信息
System.out.println("LYL_接收到客户端数据:"+info + "\n");
}
socket.shutdownInput();
//4、获取输出流 响应客户端的请求
OutputStream os= socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);//包装
pw.write("您已经连接上了\n");
pw.flush();//缓冲输出
//5、关闭资源
socket.close();
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2. 客户端代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
public static void main(String[] args) {
try {
//1、创建客户端Socket,指定服务器地址和端口
Socket socket = new Socket("localhost",12345);
//2、获取输出流,向服务器发送信息
OutputStream os = socket.getOutputStream();//字节输出流
PrintWriter pw = new PrintWriter(os);//将输出流包装为打印流
pw.write("LYL_客户端发送的内容");
pw.flush();
socket.shutdownOutput();//关闭输出流
//3、获取输入流,并且读取服务器端的响应信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info=null;
while((info = br.readLine())!=null){//循环读取服务器信息
System.out.println("LYL_我是客户端,服务器说"+info);
}
//4、关闭资源
// socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
五.UDP基本流程。
1. 服务端创建接收数据的DatagramSocket,并指定端口。
2. 服务端定义接受数据的内存空间存储数据。
3. 服务端创建用于封装数据的DatagramPacket。
4. 服务端DatagramSocket调用receive(DatagramPacket)方法开启数据监听。
5. 客户端创建DatagramSocket,并绑定本地端口。
6. 客户端创建DatagramPacket,并指定发送的数据内容,长度,远程服务IP地址及端口号。
7. 客户端调用send() 方法发送数据。
8. 服务端从DatagramPacket中获取客户端传递的数据。
9. 关闭socket连接。
六.UDP实例
1. 服务端代码:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String args[]) throws Exception{
// 定义接收数据报的DatagramSocket对象
DatagramSocket ds = null;
// 定义DatagramPacket对象
DatagramPacket dp = null;
// 开辟空间,以接收数据
byte[] buf = new byte[1024];
// 要接收数据的服务端接口
ds = new DatagramSocket(9000);
// 所有的信息使用buf保存
dp = new DatagramPacket(buf,1024) ;
// 接收数据
System.out.println("LYL_等待客户端的信息") ;
ds.receive(dp);
String str = new String(dp.getData(),0,dp.getLength()) + "from " +
dp.getAddress().getHostAddress() + ":" + dp.getPort() ;
System.out.println("LYL_" +str) ;
}
}
2. 客户端代码:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPClient {
public static void main(String args[]) throws Exception{
String str = "hello,shu xian sheng!!!";
// 定义发送数据报的对象
DatagramSocket ds = new DatagramSocket(3000);
// 声明DatagramPacket对象
DatagramPacket dp = new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("localhost"),9000) ;
System.out.println("LYL_发送") ;
ds.send(dp);
ds.close() ;
}
}