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

本文详细介绍了网络编程的基础概念,包括OSI七层模型和TCP/IP四层模型,深入讲解了网络编程中的关键要素如IP地址、端口号及协议,并通过实例演示了基于UDP和TCP协议的网络通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网络编程

A.网络编程

1)网络模型概述

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

网络模型一般是指

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

TCP/IP四层参考模型

2)网络模型7层概述


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

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

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

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

这一层的数据叫做比特

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

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

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

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

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

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

常常把这一层数据叫做段

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

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

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

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


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地址的计算机可以进行正常通讯


ipconfig /all  用来查看IP地址


getmac       用来获取mac地址


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


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传输。


2)涉及类DatagramSocket/DatagramPacket

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

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

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

DatagramPacket:数据报包

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

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


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();
	}
}
运行结果(先运行服务端,再运行客户端)

先运行服务端


再运行客户端(查看服务端控制台


查看客户端控制台


注意:只运行客户端也不会报错,因为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();
		
	}
}
客户端控制台


服务端控制台

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();
		}
	}
}

运行结果


C.TCP协议

1)概述

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

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

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

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

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

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

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


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();
		}
	}
}
运行结果

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


客户端


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();
		}
	}
}
运行结果(服务端和客户端互相对照,两台机器效果更好)

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


客户端控制台


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


服务端

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();
		}
	}
}
运行结果(对比服务端和客户端控制台,每条信息出现的时间先后顺序,加上当前毫秒值

服务端


客户端


结果

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


客户端

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();
		}
	}
}
运行结果(先运行服务测试类)


再运行客户端(多运行几次)


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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值