基础知识
- 计算机通讯:**数据从一个 IP 的 port 出发(发送方),运输到另一个 IP 的 port (接收方)**
IP 和 Port
- 每一个计算机设备上都有若干个 **网卡**,每一个网卡上有 **(全球唯一)单独硬件地址,MAC地址**,每个网卡/机器都有一个或多个 **IP 地址**
- 查看本机器(计算机)的
IP 地址
:
代码语言:txt
复制- 运行 cmd,打开 **命令行工具**
代码语言:txt
复制- 输入 **ipconfig(Window 系统)**
代码语言:txt
复制- 输入 **ifconfig(Linux/Mac 系统)**
保留 IP :127.0.0.1(代表 本机)
port:端口(逻辑上的)
代码语言:txt
复制- **范围:0~65535**
代码语言:txt
复制- **0~1023,OS 已经占有,80 是 Web,23 是 telnet**
代码语言:txt
复制- **1024~65535,任和一个程序都可以使用(注意:一个端口一次只能被一个程序使用)**
代码语言:txt
复制- 查询(端口号使用情况):
代码语言:txt
复制 - 运行 cmd,打开 **命令行工具**
代码语言:txt
复制 - **输入 netstat -an(Window/Linux/Mac 上都是)**
代码语言:txt
复制 ![在这里插入图片描述](https://img-blog.csdnimg.cn/ee12c5ab58a2484f8f30b0ed409e6840.png)
- **两台机器(电脑)的通讯就是在:IP Port 上进行的**
internet(互联网)
代码语言:txt
复制- ``网络是分层的,最外层是公网,底下的每层都是内网``
代码语言:txt
复制- 而,IP 地址可以在每个层次的网重用
- tracert 命令:查看当前机器和目标机器的访问中继
代码语言:txt
复制- 运行 cmd,打开 **命令行工具**
代码语言:txt
复制- **输入 tracert [目标机器的 IP]**
代码语言:txt
复制![在这里插入图片描述](https://img-blog.csdnimg.cn/01f81bdff0174e40bcdbb818241f921d.png)
代码语言:txt
复制- **应用层**:HTTP、TFTP、FTP、NFS、WAIS、SMTP、DNS、TeInet、SNMP、.....
代码语言:txt
复制- **传输层**:TCP、UDP
代码语言:txt
复制- **网络层**:IP、ICMP、OSPF、EIGRP、IGMP
代码语言:txt
复制- **数据链路层**:ARP、RARP、...
代码语言:txt
复制![请添加图片描述](https://img-blog.csdnimg.cn/0f4252ea07954468aeaba3c87721569b.png)
HTTP:超文本传输协议
,是现在广为流行的 WEB 网络的基础。ICMP(Internet Control Message Protocol,网络控制消息协议)
是 TCP/IP 的核心协议之一,用于在 IP 网络中发送控制消息,提供通信过程中的各种问题反馈。
代码语言:txt
复制- ICMP 直接使用 IP 数据包传输,但 ICMP 并不被视为 IP 协议的子协议。
代码语言:txt
复制- 而,常见的联网状态诊断工具依赖于 ICMP 协议。
TCP:传输控制协议
,是一种面向连接的,可靠的,基于字节流传输的通信协议。TCP 具有端口号的概念,用来标识同一个地址上的不同应用。UDP:用户数据报协议
,是一个面向数据报的传输层协议,是不可靠的。同 TCP 一样有用来标识本地应用的端口号。
Java UDP 编程
UDP(User Datagram Protocol):无连接无状态的数据报通讯协议
用户数据报协议
,**面向数据报地无连接通讯协议**(发送方发送消息后,如果接收方不在目的地,那这个消息就丢失了)- **不保证可靠**的数据传输(发送方无法得知是否发送成功)
- **速度快,也可以在较差的网络下使用**(好处是:简单、节省、经济)
实现
代码语言:txt
复制- send 和 receive 方法
代码语言:txt
复制- (可选,多网卡)绑定一个 IP 和 Port
代码语言:txt
复制- 集装箱:封装数据
代码语言:txt
复制- 地址标签:目的地 IP Port
- 注意:无主次之分,但是为了成功接受信息,
要求接收方必须早于发起方运行
代码语言:java
复制/**
* 数据 发送 与 接收 测试
*/
public class Main {
public static void main(String[] args) throws IOException {
new Thread(() -> {
// 先启动 接收方
try {
UdpInternet.takeOver(3001);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
String str1 = "hello world";
new Thread(() -> {
try {
UdpInternet.send(str1, "127.0.0.1", 3001);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* UDP 网络编程
*/
public class UdpInternet {
/**
* 接受数据
*
* @param port 端口号
*/
public static void takeOver(int port) throws IOException {
// 管道
DatagramSocket socket = new DatagramSocket(port);
byte[] bytes = new byte[1024];
// 数据集装箱
DatagramPacket packet = new DatagramPacket(bytes, 1024);
System.out.println("UdpInternet.takeOver 正在接收消息!");
System.out.println("接收端的地址 = " socket.getLocalSocketAddress());
// 接收消息,无限等待
socket.receive(packet);
System.out.println("UdpInternet.takeOver 接收到消息!");
System.out.println("数据:" new String(packet.getData(), 0, packet.getLength()));
System.out.println("发送端的地址 = " packet.getSocketAddress());
}
/**
* 发送数据(端口没有指定)
*
* @param str 消息
* @param ip IP
* @param port 端口号
*/
public static void send(String str, String ip, int port) throws IOException {
DatagramSocket socket = new DatagramSocket();
DatagramPacket packet = new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName(ip), port);
System.out.println("UdpInternet.send 正在发送消息!");
socket.send(packet);
System.out.println("UdpInternet.send 消息发送完成!");
}
}
Java TCP 编程
TCP(Transmission Control Protocol):面向连接的通讯协议
传输控制协议
- 两台机器的
可靠无差错的数据传输
- **双向字节流传递**
实现
- 第一步:**服务器创建一个 ServerSocket ,等待连接**
- 第二步:**客户机创建一个 Socket ,连接到服务器**
- 第三步:**ServerSocket 接收到连接,创建一个 Socket 和客户机的 Socket 建立专线连接,后续服务器和客户机的对话(这一对 Socket)会在一个单独的线程(服务器端)上进行**
- 第四步:**服务端的 ServerSocket 继续等待连接(可以连接多个客户机),如果有连接请求,就执行 第二步**
ServerSocket:服务器码头
代码语言:txt
复制- 需要绑定 port
代码语言:txt
复制- 如果有多块网卡,需要再绑定一个 IP 地址
代码语言:txt
复制- 客户端需要绑定服务器的地址和 port
代码语言:txt
复制- 客户端往 Socket 输入流**写入**数据,送到服务端
代码语言:txt
复制- 客户端从 Socket 输出流**获取**服务器端传过来的数据
代码语言:txt
复制- 服务器亦要如此
代码语言:java
复制/**
* TCP 测试
*/
public class ServerTest {
public static void main(String[] args) throws IOException {
// 创建服务器
new Thread(() -> {
try {
TcpServer server = new TcpServer(8001);
// 单线程
// server.openInterface();
// 多线程
TcpServer.openInterface(server);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* TCP 测试
*/
public class ClientTest1 {
public static void main(String[] args) throws IOException {
// 客户端连接服务器
new Thread(() -> {
try {
TcpClient.connect("127.0.0.1", 8001);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* TCP 测试
*/
public class ClientTest2 {
public static void main(String[] args) throws IOException {
// 客户端连接服务器
new Thread(() -> {
try {
TcpClient.connect("127.0.0.1", 8001);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
代码语言:java
复制/**
* TCP 客户端
*/
public class TcpClient {
/**
* 连接 服务端
*
* @param ip 主机 IP
* @param port 端口号
* @throws IOException IO异常
*/
public static void connect(String ip, int port) throws IOException {
// 创建 连接
Socket socket = new Socket(InetAddress.getByName(ip), port);
// 开启输入流、输出流
// 开启 通道的输入流,并包装为 缓存,方便 读入
// 客户端的输入流是:服务端的 回应
InputStream is = socket.getInputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(is));
// 开启 通道的输出流,并包装为 数据输出流,方便传输
// 输出数据给 服务端
OutputStream os = socket.getOutputStream();
DataOutputStream output = new DataOutputStream(os);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (socket.isConnected()) {
System.out.println("客户端 请输入(quit 结束):");
// 读取键盘输入
String read = reader.readLine();
if ("quit".equalsIgnoreCase(read)) {
break;
} else {
// 发送数据给服务器(在数据后加上系统的分隔符)
output.writeBytes(read System.getProperty("line.separator"));
// 获取 服务器返回的 信息
System.out.println("Server 说: " input.readLine());
}
}
// 关闭 输出流
output.close();
// 关闭 输入流
input.close();
// 关闭 键盘输入
reader.close();
// 关闭连接
socket.close();
}
}
代码语言:java
复制/**
* TCP 服务端
*/
public class TcpServer {
/**
* 码头
*/
private static ServerSocket serverSocket;
/**
* 构造函数
* <p>
* 创建 服务器连接 码头
*
* @param port 端口号
* @throws IOException IO异常
*/
public TcpServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
/**
* 创建供客户端连接的端口(多线程式)
*
* @throws IOException IO异常
*/
public static void openInterface(TcpServer server) {
try {
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
while (true) {
// 等待 客户端 请求连接
Socket accept = serverSocket.accept();
System.out.println("来了一个 Client!");
executor.submit(new Thread(new Worker(accept, server)));
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建供客户端连接的端口(单线程式)
*
* @throws IOException IO异常
*/
public void openInterface() throws IOException {
// 等待 客户端 请求连接
Socket accept = serverSocket.accept();
openInterface(accept);
}
/**
* 创建供客户端连接的端口(单线程式)
*
* @throws IOException IO异常
*/
public void openInterface(Socket accept) throws IOException {
// 开启 通道的输入流,并包装为 缓存,方便 读入
// 服务端的输入流是:客户端的 输出
InputStream is = accept.getInputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(is));
// 开启 通道的输出流
// 输出数据给 客户端
OutputStream os = accept.getOutputStream();
DataOutputStream output = new DataOutputStream(os);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (accept.isConnected()) {
// 获取 客户器发过来的 信息
// 不知道有什么方法可以检测到 客户端 连接已断开,就 先不管吧!!!!!!!!!!
// 虽然 报错
System.out.println("Client 说: " input.readLine());
System.out.println("服务端 回话(Finish 结束):");
// 读取键盘输入
String read = reader.readLine();
if ("Finish".equalsIgnoreCase(read)) {
break;
} else {
// 发送数据给客户端
output.writeBytes(read System.getProperty("line.separator"));
}
}
// 关闭 输出流
output.close();
// 关闭 输入流
input.close();
// 关闭 键盘输入
reader.close();
// 关闭连接
accept.close();
}
}
/**
* 工厂
*/
class Worker implements Runnable {
Socket socket;
TcpServer server;
public Worker(Socket socket, TcpServer server) {
this.socket = socket;
this.server = server;
}
@Override
public void run() {
try {
server.openInterface(socket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java HTTP 编程
HTTP(Hyper Text Transfer Protocol):超文本传输协议
- 而,**HTTPS 是 HTTP 的加密安全版本**。
- **HTTP 协议通过 TCP 传输,HTTP 默认使用端口 80,HTTPS 使用 443**。
访问流程
- **在浏览器输入 URL 地址**(如:https://www.baidu.com)
- **浏览器将连接到远程服务器上**(IP 80Port)
- **请求下载一个 HTML 文件下来,放到本地临时文件夹中**
- **在浏览器显示出来**
- 注意:**HTTP(超文本传输协议)、HTML(超文本标记语言)**
实现
代码语言:java
复制/**
* HTTP 测试(GET、POST)
*/
public class HttpTest {
public static void main(String[] args) {
try {
// GET
// 要访问的 url 地址
String urlName = "https://www.baidu.com";
// 封装
URL url = new URL(urlName);
System.out.println("GET 连接获得的内容:" HttpTest.doGet(url));
// POST
// 封装
String urlString = "https://tools.usps.com/go/ZipLookupAction.action";
Object userAgent = "HTTPie/0.9.2";
Object redirects = "1";
// cookie 接收策略,全部接收
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
Map<String, String> params = new HashMap<>(3);
params.put("tAddress", "1 Market Street");
params.put("tCity", "San Francisco");
params.put("sState", "CA");
String result = doPost(new URL(urlString), params,
userAgent == null ? null : userAgent.toString(),
redirects == null ? -1 : Integer.parseInt(redirects.toString()));
System.out.println("POST 连接获得的内容:" result);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 模拟 GET 方法
*
* @param url 封装好的 url
* @return 内容
* @throws IOException IO异常
*/
public static String doGet(URL url) throws IOException {
// 打开连接(获得 URL 连接)
URLConnection connection = url.openConnection();
// 建立连接
connection.connect();
System.out.println("====== 打印 http 的头部信息 ======");
Map<String, List<String>> headerFields = connection.getHeaderFields();
headerFields.forEach(
(key, value1) -> value1.stream().map(value -> key ":" value).forEach(System.out::println));
System.out.println("====== 输出收到的内容 属性信息 ======");
System.out.println("内容类型:" connection.getContentType());
System.out.println("内容长度:" connection.getContentLength());
System.out.println("内容编码:" connection.getContentEncoding());
System.out.println("日期:" connection.getDate());
System.out.println("过期:" connection.getExpiration());
System.out.println("最后修改时间:" connection.getLastModified());
// 以 UTF-8 的编码包装,读取 输入流
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
// 关闭 输入流
reader.close();
return result.toString();
}
/**
* 模拟 POST 方法
*
* @param url 封装好的 url
* @param nameValuePairs 参数键值对
* @param userAgent 用户代理
* @param redirects 重定向( >=0 false | <0 true)
* @return 内容
* @throws IOException IO异常
*/
public static String doPost(URL url, Map<String, String> nameValuePairs, String userAgent, int redirects)
throws IOException {
// 打开连接(获得 URL 连接)
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求属性
if (userAgent != null) {
connection.setRequestProperty("User-Agent", userAgent);
}
// 设置设置实例 是否 跟随重定向
if (redirects >= 0) {
connection.setInstanceFollowRedirects(false);
}
// 打开 输出流
connection.setDoOutput(true);
// 输出请求参数
try (PrintWriter out = new PrintWriter(connection.getOutputStream())) {
boolean first = true;
for (Map.Entry<String, String> pair : nameValuePairs.entrySet()) {
// 参数之间有一个 '&' 字符用于拼接
if (first) {
first = false;
} else {
out.print('&');
}
// 参数输出格式为:'key'='value'&'key1'='value1'
out.print(pair.getKey());
out.print('=');
out.print(URLEncoder.encode(pair.getValue(), StandardCharsets.UTF_8));
}
}
// 获取内容编码,如果 为空,则 设为 UTF-8
String encoding = connection.getContentEncoding();
if (encoding == null) {
encoding = "UTF-8";
}
if (redirects > 0) {
// 响应码
int responseCode = connection.getResponseCode();
System.out.println("responseCode = " responseCode);
// 响应码为:301 或 302 或 303 时
if (responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER) {
// 获取标题字段
String location = connection.getHeaderField("Location");
if (location != null) {
// url
URL base = connection.getURL();
// 断开连接
connection.disconnect();
// 重新 POST
return doPost(new URL(base, location), nameValuePairs, userAgent, redirects - 1);
}
}
} else if (redirects == 0) {
throw new IOException("Too many redirects");
}
// 获取 HTML 内容
StringBuilder response = new StringBuilder();
try (Scanner in = new Scanner(connection.getInputStream(), encoding)) {
while (in.hasNextLine()) {
response.append(in.nextLine());
response.append("n");
}
} catch (IOException e) {
InputStream err = connection.getErrorStream();
if (err == null) {
throw e;
}
try (Scanner in = new Scanner(err)) {
response.append(in.nextLine());
response.append("n");
}
}
return response.toString();
}
}
代码语言:java
复制/**
* 基于 HTTP Client(JDK 11) 的 HTTP 测试(GET、POST)
*/
public class HttpClientTest {
public static void main(String[] args) {
// GET
URI baidu = URI.create("https://www.baidu.com");
HttpResponse<String> response = doGet(baidu);
System.out.println("GET 响应内容:" (response != null ? response.body() : null));
// POST
URI usps = URI.create("https://tools.usps.com/go/ZipLookupAction.action");
HttpResponse<String> response1 = doPost(usps, "HTTPie/0.9.2");
System.out.println("POST 响应内容:" (response1 != null ? response1.body() : null));
}
/**
* GET 方法模拟
*
* @param uri URI
* @return Http 响应<String>
*/
public static HttpResponse<String> doGet(URI uri) {
try {
// 创建 HTTP 客户端
HttpClient client = HttpClient.newHttpClient();
// 生成 HTTP 请求
HttpRequest request = HttpRequest.newBuilder(uri).build();
// 客户端发送请求,返回 HTTP 应答
return client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
/**
* POST 方法模拟
*
* @param uri URI
* @param userAgent 用户代理
* @return Http 响应<String>
*/
public static HttpResponse<String> doPost(URI uri, String userAgent) {
try {
// 创建 HTTP 客户端
HttpClient client = HttpClient.newBuilder().build();
// 生成 HTTP 请求
HttpRequest request = HttpRequest.newBuilder()
// URI
.uri(uri)
// 用户代理
.headers("User-Agent", userAgent)
// 内容类型
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
// 参数 拼接
.POST(HttpRequest.BodyPublishers.ofString(
"tAddress=" URLEncoder.encode("1 Market Street", StandardCharsets.UTF_8)
"&tCity=" URLEncoder.encode("San Francisco", StandardCharsets.UTF_8)
"&sState=CA"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("response 响应码 = " response.statusCode());
System.out.println("response 标题 = " response.headers());
return response;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
代码语言:java
复制/**
* 基于 HTTP Commponents(org.apache.httpcomponents) 的 HTTP 测试(GET、POST)
* <p>
* 是一个集成的 JAVA HTTP 工具包
*/
public class HttpComponents {
public static void main(String[] args) {
// GET
HttpGet httpGet = new HttpGet("https://www.baidu.com");
System.out.println("GET 响应内容:" doGet(httpGet));
// POST
HttpPost httpPost = new HttpPost("https://tools.usps.com/go/ZipLookupAction.action");
// POST 的请求参数
List<BasicNameValuePair> list = new ArrayList<>();
list.add(new BasicNameValuePair("tAddress", URLEncoder.encode("1 Market Street", StandardCharsets.UTF_8)));
list.add(new BasicNameValuePair("tCity", URLEncoder.encode("San Francisco", StandardCharsets.UTF_8)));
list.add(new BasicNameValuePair("sState", "CA"));
System.out.println("POST 响应内容:" doPost(httpPost, list, "HTTPie/0.9.2"));
}
/**
* GET 方法模拟
*
* @param httpGet HttpGet
* @return String
*/
public static String doGet(HttpGet httpGet) {
// 创建 HTTP 客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
// 请求配置
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时
.setConnectTimeout(5000)
// 设置连接请求超时
.setConnectionRequestTimeout(5000)
// 设置套接字超时
.setSocketTimeout(5000)
// 启用 重定向
.setRedirectsEnabled(true)
.build();
// 加入 请求配置
httpGet.setConfig(requestConfig);
// 获取 返回的内容
StringBuilder result = new StringBuilder();
try {
// 执行 GET
CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
// 如果请求成功了
if (httpResponse.getStatusLine().getStatusCode() == 200) {
System.out.println("code:200");
result.append(EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8));
} else {
System.out.println("请求失败!响应码:" httpResponse.getStatusLine().getStatusCode());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result.toString();
}
/**
* 模拟 POST 请求
*
* @param httpPost HTTP post
* @param list 请求参数键值对
* @param userAgent 用户代理
* @return String
*/
public static String doPost(HttpPost httpPost, List<BasicNameValuePair> list, String userAgent) {
// 获取 HTTP Client
CloseableHttpClient httpClient = HttpClientBuilder.create()
// 设置重定向策略(松散的重定向策略)
.setRedirectStrategy(new LaxRedirectStrategy()).build();
// 请求配置
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时
.setConnectTimeout(10000)
// 设置连接请求超时
.setConnectionRequestTimeout(10000)
// 设置套接字超时
.setSocketTimeout(10000)
// 不启用 重定向
.setRedirectsEnabled(false)
.build();
// 加入 请求配置
httpPost.setConfig(requestConfig);
// 获取 返回的内容
StringBuilder result = new StringBuilder();
try {
// Url 编码的表单实体(用于装入 POST 请求的参数)
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
// 将表单加入到 HTTP POST 中
httpPost.setEntity(entity);
// 加入用户代理
httpPost.setHeader("User-Agent", userAgent);
// 执行 POST
HttpResponse httpResponse = httpClient.execute(httpPost);
// 如果请求成功了
if (httpResponse.getStatusLine().getStatusCode() == 200) {
System.out.println("code:200");
result.append(EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8));
} else {
System.out.println("Error 响应码:" httpResponse.getStatusLine().getStatusCode());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result.toString();
}
}