博主主页: 码农派大星.
数据结构专栏:Java数据结构
数据库专栏:MySQL数据库
JavaEE专栏:JavaEE
关注博主带你了解更多数据结构知识
1.网络初识
1.1基本知识
网络的发展:单机时代-局域网时代-广域网时代-移动互联网时代
局域网:把几个电脑通过一个路由器连接到一起
广域网:把更多的局域网连接在在一起
IP地址:IP地址是 IP协议 提供的一种统一的 地址格式 ,它为互联网上的每一个网络和每一台 主机 分配一个逻辑地址
端口号:不同程序关联了不同的端口号,一个端口号只能绑定一个程序但是一个程序可以绑定多个端口号。
协议:一种约定
五元组:源IP地址,目的IP地址,源端口号,目的端口号,协议类型
协议分层:如果搞一个大的协议来解决所有问题,那么这个协议就会非常的胖达,所以我们就进行拆分,由于通信实在太复杂,拆分出来的小协议就非常多,所以就进行了分层,约定了不同层次之间的调用关系,上层协议调用下层协议,下层协议给上层协议提供支持。
1.2.OSI与TCP/IP
OSI 七层模型既复杂⼜不实⽤:所以OSI七层模型没有落地、实现. 实际组建⽹络时,只是以OSI七层模型设计中的部分分层,也即是以下TCP/IP五层(或四层)模型来 实现。
TCP/IP五层(或四层)模型
TCP/IP是⼀组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇。TCP/IP通讯协议采⽤了5层的层级结构,每⼀层都呼叫它的下⼀层所提供的⽹络来完成⾃⼰的需求。
• 应⽤层:负责应⽤程序间沟通,如简单电⼦邮件传输(SMTP)、⽂件传输协议(FTP)、⽹络远 程访问协议(Telnet)等。我们的⽹络编程主要就是针对应⽤层。
• 传输层:负责两台主机之间的数据传输。如传输控制协议(TCP),能够确保数据可靠的从源主机发 送到⽬标主机。
• ⽹络层:负责地址管理和路由选择。例如在IP协议中,通过IP地址来标识⼀台主机,并通过路由表 的⽅式规划出两台主机之间的数据传输的线路(路由)。路由器(Router)⼯作在⽹路层。
• 数据链路层:负责设备之间的数据帧的传送和识别。例如⽹卡设备的驱动、帧同步(就是说从⽹线上 检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就⾃动重发)、数据差错校验等⼯作。 有以太⽹、令牌环⽹,⽆线LAN等标准。交换机(Switch)⼯作在数据链路层。
• 物理层:负责光/电信号的传递⽅式。⽐如现在以太⽹通⽤的⽹线(双绞线)、早期以太⽹采⽤的的同 轴电缆(现在主要⽤于有线电视)、光纤,现在的wifi⽆线⽹使⽤电磁波等都属于物理层的概念。物理 层的能⼒决定了最⼤传输速率、传输距离、抗⼲扰性等。集线器(Hub)⼯作在物理层。
⽹络设备所在分层
• 对于⼀台主机,它的操作系统内核实现了从传输层到物理层的内容,也即是TCP/IP五层模型的下四 层;
• 对于⼀台路由器,它实现了从⽹络层到物理层,也即是TCP/IP五层模型的下三层;
• 对于⼀台交换机,它实现了从数据链路层到物理层,也即是TCP/IP五层模型的下两层;
• 对于集线器,它只实现了物理层;
2.网络编程
⽹络编程,指⽹络上的主机,通过不同的进程,以编程的⽅式实现⽹络通信(或称为⽹络数据传 输)我们只要满⾜进程不同就⾏;所以即便是同⼀个主机,只要是不同进程,基于⽹络来传输数 据,也属于⽹络编程。
但对于开发来说,在条件有限的情况下,⼀般也都是在⼀个主机中运⾏多个进程来完成⽹络编 程。
2.1 发送端和接收端
在⼀次⽹络数据传输时:
发送端:数据的发送⽅进程,称为发送端。发送端主机即⽹络通信中的源主机。
接收端:数据的接收⽅进程,称为接收端。接收端主机即⽹络通信中的⽬的主机。
收发端:发送端和接收端两端,也简称为收发端。
2.2请求和响应
获取⼀个⽹络资源,涉及到两次⽹络数据传输:
• 第⼀次:请求数据的发送
• 第⼆次:响应数据的发送。
2.3客⼾端和服务端
服务端: 在常⻅的⽹络数据传输场景下,把提供服务的⼀⽅进程,称为服务端,可以提供对外服务。
客⼾端:获取服务的⼀⽅进程,称为客⼾端。
2.4 TCP与UDP区别
传输层提供了2个协议是:TCP和UDP,那有什么区别呢?
TCP是有连接的,UDP是无连接的:TCP保存了对方信息,UDP没有保存
TCP是可靠的,UDP是不可靠的:TCP消息发出能够知道是否到达,UDP不知道。
TCP是面向字节流,UDP是面向数据报:TCP以字节为单位传输,UDP以数据报为单位进行传输
TCP和UDP都是全双工:允许双向通信
3.Socket API编程
3.1 UDP网络编程
API接口介绍:
服务端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class udpechoServer {
private DatagramSocket socket = null;
public udpechoServer (int port) throws SocketException {
socket = new DatagramSocket(port);
}
//通过start启动服务器的核心流程
public void start() throws IOException {
System.out.println("服务器启动!");
while(true){
//此时通过"死循环"不停处理客户端需求
//1,读取客户端的请求并解析
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
//上述收到数据,为二级制byte[]的形式体现的,后续代码如果要进行打印之类的操作
//需要转成字符串才好处理
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
//2.根据请求计算响应,由于此处是回显服务器,响应就是请求
String response = process(request);
//3.把响应写回客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(requestPacket);
//4.打印日志
System.out.printf("[%s:%d] req = %s, resp=%s\n",requestPacket.getAddress(),requestPacket.getPort(),
request,response);
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
udpechoServer server = new udpechoServer(9090);
server.start();
}
}
客户端:
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class udpEchoClient {
DatagramSocket socket = null;
private String serverIP;
private int serverPort;
public udpEchoClient(String serverIP,int serverPort) throws SocketException {
socket = new DatagramSocket();
this.serverIP = serverIP;
this.serverPort = serverPort;
}
public void start() throws IOException {
System.out.println("启动客户端");
Scanner scanner = new Scanner(System.in);
while (true){
//1.从控制台读取到用户输入
//2构造出一个udp请求,发送给服务器
//3从服务器读取到响应
//4把响应打印到控制台
System.out.println("->");
String request = scanner.next();
DatagramPacket requestPacket = new
DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(this.serverIP), this.serverPort);
socket.send(requestPacket);
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.printf(response);
}
}
public static void main(String[] args) throws IOException {
udpEchoClient client = new udpEchoClient("192.168.0.108",9090);
client.start();
}
}
运行结果:
3.2 TCP网络编程
API接口介绍:
服务端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpEchoServer {
private ServerSocket serverSocket = null;
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("启动服务器!");
ExecutorService service = Executors.newCachedThreadPool();
while (true){
Socket cilentSocket = serverSocket.accept();
//创建线程,每个线程服务一个客户端
/* Thread t = new Thread(()->{
try {
processConnection(cilentSocket);
} catch (IOException e) {
e.printStackTrace();
}
});
t.start();*/
//使用线程池
service.submit(()->{
try {
processConnection(cilentSocket);
}catch (IOException e){
throw new RuntimeException(e);
}
});
}
}
//针对一个连接,提供处理逻辑
private void processConnection(Socket cilentSocket) throws IOException {
//先来打印一下客户端信息
System.out.printf("[%s:%d] 客户端上线!\n",cilentSocket.getInetAddress(),
cilentSocket.getPort() );
try(InputStream inputStream = cilentSocket.getInputStream();
OutputStream outputStream = cilentSocket.getOutputStream()){
//使用Scanner包装一下Inputstream,就可以更方便的读取这里的请求数据了
Scanner scanner = new Scanner(inputStream);
PrintWriter printWriter = new PrintWriter(outputStream);
while (true){
//1,读取请求并解析
if(!scanner.hasNext()){
//如果scanner无法读取出数据,说明客户端关闭了连接,导致服务器这边读取到"末尾"
break;
}
String request = scanner.next();
//2,根据请求计算响应
String response = process(request);
//3,把响应写回客户端
//此处可以按照字节数组直接来写,也可以有另外一种写法
//outputStream.write(response.getBytes());
printWriter.println(response);
printWriter.flush();
//4,打印日志
System.out.printf("[%s:%d] req = %s; resq = %s\n",cilentSocket.getInetAddress(),
cilentSocket.getPort(),request,response);
}
}catch (IOException e){
e.printStackTrace();
}finally {
System.out.printf("[%s:%d] 客户端下线!\n",cilentSocket.getInetAddress(),
cilentSocket.getPort());
cilentSocket.close();
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
客户端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoClient {
private Socket socket = null;
public TcpEchoClient(String serverIp,int serverPort) throws IOException {
socket = new Socket(serverIp,serverPort);
}
public void start(){
System.out.println("客户端启动!");
try(InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()){
Scanner scanner = new Scanner(inputStream);
Scanner scannerIn = new Scanner(System.in);
PrintWriter printWriter = new PrintWriter(outputStream);
while (true){
//1.从控制台读取服务器
System.out.println("->");
String request = scannerIn.next();
//2.把请求发送给服务器
printWriter.println(request);
printWriter.flush();
//3.从服务器读取响应
if(!scanner.hasNext()){
break;
}
String response = scanner.next();
//4,打印响应结果
System.out.println(response);
}
}catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
client.start();
}
}