【JavaSE学习笔记】网络编程_UDP协议,TCP协议

时间:2022-06-16 10:28:02

网络编程

A.网络编程

1)网络模型概述

计算机网络之间以何种规则进行通信,就是网络模型研究问题。

网络模型一般是指

OSI(Open System Interconnection开放系统互连)七层参考模型

TCP/IP四层参考模型

2)网络模型7层概述

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

1.物理层:主要定义物理设备标准

如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等

它的主要作用是传输比特流

就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0

这一层的数据叫做比特

2.数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装

常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输

3.网络层:主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装

在这一层工作的设备是路由器,常把这一层的数据叫做数据包

4.传输层:定义了一些传输数据的协议和端口号(WWW端口80等)

主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。

常常把这一层数据叫做段

5.会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。

主要在你的系统之间发起会话或者接受会话请求

6.表示层:主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等

7.应用层:主要是一些终端的应用

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

3)网络编程的三要素

1.IP地址

a.作用:用来在互联网上标示一个计算机的唯一性

b.由来:我们计算机只能识别二进制数据,因此我们的IP地址应该也是一个二进制数据.32位

比如: 00000100. 00000010 .00001000 .00000001

如果我们使用上面的标示一个IP地址,那么我们记忆起来就比较麻烦.

于是我们就把上边的而二进制数据使用"."进行隔开

每一个字节隔开.在把每一个字节转换成十进制数据

于是上边的IP地址就可以转换为: 4.2.8.1

于是上边的IP地址就可以转换为: 4.2.8.1

c.IP地址的组成:IP地址 = 网络地址 + 主机地址

A类IP地址:第一段号码为网络地址,剩下的三段号码为本地计算机的号码

能配 256 * 256 * 256 = 16777216 台电脑

B类IP地址:前二段号码为网络地址,剩下的二段号码为本地计算机的号码

能配 256 * 256 = 65536 台电脑

C类IP地址:前三段号码为网络地址,剩下的一段号码为本地计算机的号码

256  公司用的比较多

d.IP地址的分类:

A类 1.0.0.1---127.255.255.254

B类 128.0.0.1---191.255.255.254172.16.0.0---172.31.255.255是私有地址。

C类 192.0.0.1---223.255.255.254192.168.X.X是私有地址 只能用于局域网

D类 224.0.0.1---239.255.255.254

E类 240.0.0.1---247.255.255.254

e.特殊的IP地址:

127.0.0.1 本地回环地址用来做一些本地测试不走交换机

ping IP地址; 用来检测本机是否可以和指定的IP地址的计算机可以进行正常通讯

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

ipconfig /all  用来查看IP地址

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

getmac       用来获取mac地址

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

2. 端口号

物理端口:物理设备对应的端口, 网卡口

逻辑端口:用来标示我们的计算机上的进程(正在运行的程序)  

端口号的有效范围应该是 0-65535,其中0-1024被系统占用或者保留

3.协议:UDP/TCP

面试题:两者区别

UDP:

把数据打成一个数据包 , 不需要建立连接

数据包的大小有限制不能超过64k

因为无连接,所以属于不可靠协议(可能丢失数据)

因为无连接 ,所以效率高

TCP:

需要建立连接,形成连接通道

数据可以使用连接通道直接进行传输,无大小限制

因为有链接,所以属于可靠协议

因为有链接,所以效率低

4)InerAddress

1.概述

为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress 供我们使用

此类表示互联网协议 (IP) 地址

2.常见功能

public static InetAddress getByName(String host)

封装一个主机:可以是主机名,也可以是IP地址的字符串表现形式

查看主机名:hostname

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

public String getHostAddress()://获取IP

public String getHostName()://获取主机名

import java.io.IOException;
import java.net.InetAddress;

public class IPDemo {
public static void main(String[] args) throws IOException {
// 创建InerAddress类对象
// 通过hostname命令查找主机名
InetAddress address = InetAddress.getByName("WPF-PC");

// 获取ip
String ip = address.getHostAddress();
System.out.println(ip);// 192.168.31.122

// 获取主机名
String name = address.getHostName();
System.out.println(name);// WPF-PC
}
}

B.UDP协议

1)Socket通信原理

Socket=IP+端口号

1.Socket套接字概述

网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。

2.Socket原理机制

通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

2)涉及类DatagramSocket/DatagramPacket

DatagramSocket:此类表示用来发送和接收数据报包的套接字

构造方法:DatagramSocket(int port, InetAddress laddr)

创建数据报套接字,将其绑定到指定的本地地址。

DatagramPacket:数据报包

构造方法:DatagramPacket(byte[] buf, int length, InetAddress address, int port)

用来将长度为 length 的包发送到指定主机上的指定端口号

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

3)UDP协议发送数据/接收数据

方便演示(我建立单机通讯,有局域网的,可以建立双机通讯)

先建立服务器端(接收端)

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPSrver {
public static void main(String[] args) throws IOException {
// 创建服务端对象:端口号一直
DatagramSocket ds = new DatagramSocket(9527);

// 按照字节数组接收数据
byte[] bys = new byte[1024];
int length = bys.length;

// 创建数据报包对象用来接收数据
DatagramPacket dp = new DatagramPacket(bys, length);

// 接收数据
System.out.println("服务器已开启,等待中...");
ds.receive(dp);// 服务端会卡在这里,直到客户端连接
System.out.println("数据传输完毕...");

// 获取传输来源
String ip = dp.getAddress().getHostAddress();// 客户端ip
String name = dp.getAddress().getHostName();// 客户端主机名

// 解析数据
byte[] data = dp.getData();
// 获取数据实际长度
int length2 = dp.getLength();
System.out.println(new String(data, 0, length2));
System.out.println("客户端主机名:" + name);
System.out.println("客户端ip地址:" + ip);

// 释放资源
ds.close();
}
}

后建立客户端(发送端)

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClicent {
public static void main(String[] args) throws IOException {
// 封装服务端主机 查找服务端主机ip: ipconfig /all
InetAddress address = InetAddress.getByName("192.168.31.122");

// 创建客户端对象
DatagramSocket ds = new DatagramSocket();

// 传出数据
byte[] bys = "我爱你中国".getBytes();
int length = bys.length;

// 创建数据报包对象 端口号要一致
DatagramPacket dp = new DatagramPacket(bys, length, address, 9527);

// 发送数据
ds.send(dp);
System.out.println("发送完毕!");

// 释放资源
ds.close();
}
}
运行结果(先运行服务端,再运行客户端)

先运行服务端

【JavaSE学习笔记】网络编程_UDP协议,TCP协议
再运行客户端(查看服务端控制台

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

查看客户端控制台

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

注意:只运行客户端也不会报错,因为UDP不需要连接,至发送出去,至于能不能接收到不管

4)改进:UDP协议发送端的数据来自于键盘录入

服务端(不需要释放资源,服务器正常情况下不会关闭)

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServer {
public static void main(String[] args) throws IOException {
// 创建服务端对象:端口号一直
DatagramSocket ds = new DatagramSocket(9527);
while (true) {
// 按照字节数组接收数据
byte[] bys = new byte[1024];
int length = bys.length;

// 创建数据报包对象用来接收数据
DatagramPacket dp = new DatagramPacket(bys, length);

// 接收数据
System.out.println("服务器已开启,等待中...");
ds.receive(dp);// 服务端会卡在这里,直到客户端连接
System.out.println("数据传输完毕...");

// 获取传输来源
String ip = dp.getAddress().getHostAddress();// 客户端ip
String name = dp.getAddress().getHostName();// 客户端主机名

// 解析数据
byte[] data = dp.getData();
// 获取数据实际长度
int length2 = dp.getLength();
System.out.println(new String(data, 0, length2));
System.out.println("客户端主机名:" + name);
System.out.println("客户端ip地址:" + ip);
}
}
}

客户端

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class UDPClicent {
public static void main(String[] args) throws IOException {
// 创建键盘录入对象
Scanner sc = new Scanner(System.in);

// 封装服务端主机
InetAddress address = InetAddress.getByName("192.168.31.122");

// 创建客户端对象
DatagramSocket ds = new DatagramSocket();

// 传输数据
String str = null;
System.out.println("请输入消息:");
while ((str = sc.next()) != null) {
System.out.println("请输入消息:");
byte[] bys = str.getBytes();
int length = bys.length;

// 创建数据报包对象
DatagramPacket dp = new DatagramPacket(bys, length, address, 9527);

// 传输数据
ds.send(dp);
}

// 释放资源
ds.close();

}
}
客户端控制台

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

服务端控制台
【JavaSE学习笔记】网络编程_UDP协议,TCP协议

5)改进:多线程改进聊天小程序(同一个控制台输出

服务端

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class MyServer extends Thread {
// 传递端口变量
private DatagramSocket ds;

public MyServer(DatagramSocket ds) {
super();
this.ds = ds;
}

@Override
public void run() {
try {
while (true) {
// 接收字节数组
byte[] bys = new byte[1024];
int length = bys.length;
// 创建数据报包对象
DatagramPacket dp = new DatagramPacket(bys, length);
// 接收数据
ds.receive(dp);;
// 解析数据
byte[] bs = dp.getData();
int length2 = dp.getLength();
String ip = dp.getAddress().getHostAddress();
System.out.println(new String(bs, 0, length2));
System.out.println("客户端ip:" + ip);
System.out.println("-----------------------");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class MyClicent extends Thread {
// 传递端口变量
private DatagramSocket ds;

public MyClicent(DatagramSocket ds) {
super();
this.ds = ds;
}

@Override
public void run() {
try {
// 创建键盘录入对象
Scanner sc = new Scanner(System.in);
// 封装服务端主机
InetAddress address = InetAddress.getByName("192.168.31.122");
// 输入消息
String str = null;
System.out.println("请输入消息:");
while ((str = sc.next()) != null) {
byte[] bys = str.getBytes();
int length = bys.length;
// 创建数据报包对象
DatagramPacket dp = new DatagramPacket(bys, length, address, 9527);
// 传输
ds.send(dp);
System.out.println("-----------------------");
// 继续输入
System.out.println("请输入消息:");
}
// 释放资源
ds.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

测试类

import java.net.DatagramSocket;
import java.net.SocketException;

public class UDPDemo {
public static void main(String[] args) {
try {
// 创建客户端对象
DatagramSocket Client = new DatagramSocket();
// 创建服务端对象 定义服务端接口
DatagramSocket Reverse = new DatagramSocket(9527);
// 开启客户端线程
new MyClicent(Client).start();
// 开启服务端线程
new MyServer(Reverse).start();
} catch (SocketException e) {
e.printStackTrace();
}
}
}

运行结果

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

C.TCP协议

1)概述

ServerSocker服务端:此类实现服务器套接字。

服务器套接字等待请求通过网络传入

它基于该请求执行某些操作,然后可能向请求者返回结果。

Socker客户端:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。

方法:Socket accept():侦听,有客户端连接,程序继续运行

           Socket getInputStream():输入流,读取数据

   Socket getOutputStream():输出流,写出数据

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

2)TCP协议发送数据/接收数据

(先服务端,后客户端。只运行服务端会报错,关闭先关闭客户端

服务端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
public static void main(String[] args) {
try {
// 创建服务端的 端口
ServerSocket ss = new ServerSocket(9527);

// 侦听客户端
System.out.println("服务器已开启,等待客户端连接...");
Socket sk = ss.accept();// 服务器开启以后会卡在这里,等待客户端连接
System.out.println("客户端连接成功...");

// 读取数据流
InputStream in = sk.getInputStream();

// 获取ip
String ip = sk.getInetAddress().getHostAddress();

// 创建缓冲区,读取数据
byte[] bys = new byte[1024 * 8];// 习惯这么写
int len = in.read(bys);
System.out.println(new String(bys, 0, len));
System.out.println("客户端ip:" + ip);

// 收到消息后,反馈客户端
OutputStream out = sk.getOutputStream();
out.write("消息已经收到!".getBytes());

// 释放资源
sk.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class MyClicent {
public static void main(String[] args) {
try {
// 创建客户端对象
Socket sk = new Socket("192.168.31.122", 9527);
System.out.println("成功连接服务端,准备发送数据...");

// 获取输出流
OutputStream out = sk.getOutputStream();

// 写数据
out.write("我爱你中国".getBytes());
System.out.println("发送完毕,等待服务端接收确认...");

// 读取反馈
InputStream in = sk.getInputStream();
byte[] bys = new byte[1024 * 8];
int length = bys.length;
System.out.println(new String(bys, 0, length));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果

服务端(运行后先出现第一行,客户端一运行,服务端就出现第二行,然后接收数据)

【JavaSE学习笔记】网络编程_UDP协议,TCP协议
客户端

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

3)方法

Socket shutdownOutput():结束输出

输出通道中判断结束条件 -1/null不顶用

4)需求:聊天对话

服务端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class MyServer {
public static void main(String[] args) {
try {
// 创建Scanner对象
Scanner sc = new Scanner(System.in);

// 创建服务端的 端口
ServerSocket ss = new ServerSocket(9527);

// 侦听客户端
Socket sk = ss.accept();// 服务器开启以后会卡在这里,等待客户端连接

// 读取数据流
InputStream in = sk.getInputStream();
OutputStream out = sk.getOutputStream();

while (true) {
// 创建缓冲区,读取数据
byte[] bys = new byte[1024 * 8];
int len = in.read(bys);
System.out.println(new String(bys, 0, len));

// 写数据
String str = sc.next();
out.write(str.getBytes());
out.flush();
}

} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class MyClicent {
public static void main(String[] args) {
try {
// 创建键盘录入对象
Scanner sc = new Scanner(System.in);

// 创建客户端对象
Socket sk = new Socket("192.168.31.122", 9527);

// 获取通道流
OutputStream out = sk.getOutputStream();
InputStream in = sk.getInputStream();

while (true) {
// 写数据
String str = sc.next();
out.write(str.getBytes());
out.flush();

// 读数据
byte[] bys = new byte[1024 * 8];
int length = in.read(bys);
System.out.println(new String(bys, 0, length));
}

} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果(服务端和客户端互相对照,两台机器效果更好)

服务端控制台(白色为接收的数据,蓝色为发出的数据)

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

客户端控制台

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

5)需求:将客户端文件上传到服务端,服务端并接收

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

服务端

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyReverse {
public static void main(String[] args) {
try {
// 创建服务器端口
ServerSocket ss = new ServerSocket(9527);

// 侦听
System.out.println("服务器开启,等待客户端连接!");
Socket sk = ss.accept();
System.out.println("客户端连接成功,正在接收数据!");

// 获取通道流
InputStream in = sk.getInputStream();
OutputStream out = sk.getOutputStream();

// 包装字节缓冲流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("eclipse.exe"));
BufferedInputStream bis = new BufferedInputStream(in);

// 接收数据
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}

System.out.println("文件接收完毕!");

// 反馈客户端
out.write("文件接收完毕!".getBytes());
out.flush();

// 释放资源
ss.close();// 服务器一般不释放资源

} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class MyClicent {
public static void main(String[] args) {
try {
// 封装服务端主机
InetAddress address = InetAddress.getByName("192.168.31.122");

// 创建客户端对象
Socket sk = new Socket(address, 9527);
System.out.println("成功连接服务器,开始上传文件!");

// 获取通道流
InputStream in = sk.getInputStream();
OutputStream out = sk.getOutputStream();

// 包装字节缓冲流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("eclipse-inst-win64.exe"));
BufferedOutputStream bos = new BufferedOutputStream(out);

// 传输数据
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}

// 传输完毕
sk.shutdownOutput();
System.out.println("文件上传成功,等待服务端确认!");

// 接受反馈
byte[] by = new byte[1024];
int lin = in.read(by);
System.out.println(new String(by, 0, lin));

// 释放资源
sk.close();

} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果(对比服务端和客户端控制台,每条信息出现的时间先后顺序, 加上当前毫秒值

服务端

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

客户端

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

结果
【JavaSE学习笔记】网络编程_UDP协议,TCP协议

6)需求:多个客户端给服务器上传文本文件

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

客户端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class MyClicent {
public static void main(String[] args) {
try {
// 创建客户端对象
Socket sk = new Socket("192.168.31.122", 9527);
// 获取通道流
InputStream in = sk.getInputStream();
OutputStream out = sk.getOutputStream();
// 包装流
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
// 传输
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// 传输完毕
sk.shutdownOutput();

// 接收反馈
byte[] bys = new byte[1024];
int len = in.read(bys);
System.out.println(new String(bys, 0, len));

// 释放资源
sk.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

服务端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class UpLoadThread extends Thread {
private Socket sk;

public UpLoadThread(Socket sk) {
super();
this.sk = sk;
}

@Override
public void run() {
try {
// 获取通道流
InputStream in = sk.getInputStream();
OutputStream out = sk.getOutputStream();
// 包装流
BufferedReader br = new BufferedReader(new InputStreamReader(in));
BufferedWriter bw = new BufferedWriter(new FileWriter(System.currentTimeMillis() + "b.txt"));
// 读写
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}

// 反馈
out.write("文件上传完毕!".getBytes());

} catch (IOException e) {
e.printStackTrace();
}
}
}

服务端测试类

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MyReverse {
public static void main(String[] args) {
try {
// 创建服务端对象
ServerSocket ss = new ServerSocket(9527);

// 侦听
System.out.println("服务器已开启,等待客户端连接...");

while (true) {
Socket sk = ss.accept();
// 开启线程
new UpLoadThread(sk).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果(先运行服务测试类)

【JavaSE学习笔记】网络编程_UDP协议,TCP协议
再运行客户端(多运行几次)

【JavaSE学习笔记】网络编程_UDP协议,TCP协议

刷新工程(运行多少次,出现多少结果,为防止同名覆盖,加上上传时间System.currentTimeMillis())

【JavaSE学习笔记】网络编程_UDP协议,TCP协议