Java 网络编程
概述
- 主要介绍OSI模型和TCP/IP模型,网络协议,以及socket网络编程
什么是网络编程
- 网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。中间最主要的就是数据包的组装,数据包的过滤,数据包的捕获,数据包的分析。
OSI模型
OSI参考模型的7个层次
-
物理层
物理层涉及通信信道上传输的原始比特流(bits),主要功能是为数据端设备提供传送数据的通路以及传输数据
-
数据链路层
数据链路层的主要任务是实现计算机网络中相邻节点之间的可靠传输,把原始的、有差错的物理传输线路加上数据链路协议以后,构成逻辑上可靠的数据链路。需要完成的功能有链路管理、成帧、差错控制以及流量控制等。其中成帧是对物理层的原始比特流进行界定,数据链路层也能够对帧的丢失进行处理
-
网络层
网络层涉及源主机节点到目的主机节点之间可靠的网络传输,它需要完成的功能主要包括路由选择、网络寻址、流量控制、拥塞控制、网络互连等。
-
传输层
传输层涉及源端节点到目的端节点之间可靠的信息传输。传输层需要解决跨越网络连接的建立和释放,对底层不可靠的网络,建立连接时需要三次握手,释放连接时需要四次挥手
-
会话层
会话层的主要功能是负责应用程序之间建立、维持和中断会话,同时也提供对设备和结点之间的会话控制,协调系统和服务之间的交流,并通过提供单工、半双工和全双工3种不同的通信方式,使系统和服务之间有序地进行通信
-
表示层
主要功能是把应用层提供的信息变换为能够共同理解的形式,提供字符代码、数据格式、控制信息格式、加密等的统一表示
-
应用层
应用层为OSI的最高层,是直接为应用进程提供服务的。其作用是在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需的服务
TCP/IP模型
TCP/IP协议是一个开放的网络协议簇(常见tcp,udp),TCP/IP协议定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准
-
网络接口层 (链路层)
网络接口层对应着物理层和数据链路层
-
互联网层 ( IP层 )
互联网层是整个TCP/IP协议栈的核心,功能是把分组发往目标网络或主机,同时,为了尽快地发送分组,可能需要沿不同的路径同时进行分组传递。也可以完成将不同类型的网络(异构网)互连的任务
-
传输层 ( TCP层 )
TCP层负责在应用进程之间建立端到端的连接和可靠通信,它只存在与端节点中。TCP层涉及两个协议,TCP和UDP
-
应用层
应用层为Internet中的各种网络应用提供服务。
TCP/IP结构对应OSI
TCP/IP | OSI |
---|---|
应用层 | 应用层 <br> 表示层 <br> 会话层 |
传输层 ( TCP层 ) | 传输层 |
互联网层 ( IP层 ) | 网络层 |
网络接口层 (链路层) | 数据链路层 <br>物理层 |
常见网络协议
TCP协议
- TCP(Transmission Control Protocol ,传输控制协议)是面向连接的传输层协议。TCP层是位于IP层之上,应用层之下的中间层,采用字节流传输数据。
三次握手
-
Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认
-
Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态
-
Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态
四次挥手
- 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。当一方发送一个FIN来终止这个方向的连接,收到 FIN只意味着这个方向上没有数据流动,当TCP连接在收到一个FIN后仍能发送数据
-
Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态
-
Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态
-
Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态
-
Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手
UDP协议
- 用户数据报协议,英文全称是User Datagram Protocol,它是TCP/IP协议簇中无连接的运输层协议
- 由两部分组成:首部和数据。其中,首部仅有8个字节,包括源端口和目的端口,长度(UDP用于数据报的长度)、校验和
HTTP协议
- 超文本传输协议,英文全称是Hypertext Transfer Protocol
- HTTP是一种应用层协议,它是基于TCP协议之上的请求/响应式的协议
TCP和UDP区别
- TCP面向连接,UDP是无连接
- TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证
- TCP面向字节流,UDP是面向报文的
- TCP连接只能是点到点的,UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节,UDP的首部开销只有8个字节
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
Socket 编程
Socket 服务端实现
import java.net.*;
import java.io.*;
public class SocketServer extends Thread {
private ServerSocket serverSocket;
public SocketServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(100000);
}
public void run() {
while (true) {
try {
System.out.println("服务器监听端口:" + serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("客户端地址:" + server.getRemoteSocketAddress());
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println("客户端发送内容:" + in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("Hello client ");
server.close();
} catch (SocketTimeoutException s) {
System.out.println("连接超时");
break;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String[] args) {
int port = 8000;
try {
new SocketServer(port).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket 客户端实现
import java.net.*;
import java.io.*;
public class SocketClient {
public static void main(String[] args) {
String serverName = "localhost";
int port = 8000;
try {
Socket client = new Socket(serverName, port);
System.out.println("服务器地址:" + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello server ");
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("服务器响应: " + in.readUTF());
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}