JAVA进阶 —— 网络编程

目录

一、什么是网络编程

1.  常见的软件架构

二、网络编程三要素

1. IP

1.1  IPv4

 1.2  IPv6

 2. 端口号

3. 协议

3.1  UDP协议

3.2  TCP协议

三、UDP通信程序

 1. 发送数据

2. 接收数据

3. UDP三种通信方式

四、TCP通信程序

4.1  中文乱码问题

4.2  三次握手和四次挥手

 五、综合练习

1.  多收多发

2.  接收和反馈

3.  上传文件

4.  上传文件(文件名重复问题)

5.  上传文件(多线程版)

6.  上传文件(线程池优化)


一、什么是网络编程

在网络通信协议下,不同的计算机上运行的程序,进行的数据传输。

  • 应用场景:即时通信、网络对战、金融证券、国际贸易、邮件等等。不管是什么样的场景,都是计算机计算机之间通过网络进行的数据传输。
  • Java中可以使用 java.net 包下的技术轻松开发出常见的网络应用程序。

1.  常见的软件架构

在用户本地需要下载并安装客户端程序,在远程有一个服务器端程序。
只需要一个浏览器,用户通过不同的网址。客户访问不同的服务器。

CS:客户端服务端模式需要开发客户端。

BS:浏览器服务端模式不需要开发客户端。

CS:适合定制专业化的办公类软件如:IDEA、网游。

BS:适合移动互联网应用,可以在任何地方随时访问的系统。

二、网络编程三要素

  •  IP:设备在网络中的地址,是唯一的标识。
  • 端口号:应用程序在设备中的唯一的标识。
  • 协议:数据在网络中传输的规则,常见的协议有UDP、TCP、http、https、ftp。

1. IP

   全称:Internet Protocol,是互联网协议地址,也成IP地址。

IP是分配给上网设备的数字标签。

常见的IP分类为:

  • IPv4,IPv6

1.1  IPv4

全称:Internet Protocol version 4,互联网通信协议第四版

采用32位地址长度,分为4组。

 IPv4的地址分类形式:

  • 公司地址(万维网使用)和私有地址(局域网使用)。
  • 192.168. 开头的就是私有地址,范围即为192.168.0.0 --- 192.168.255.255,专门为组织结构内部使用,以此节省IP。
  • 特殊IP地址:127.0.0.1,也可以是localhost:是回送地址也成本地回环地址,也成本机IP,永远只会寻找当前所在本机。
  • 常见的CMD命令:
    ipconfig 查看本机IP地址
    ping 检查网络是否连通

 1.2  IPv6

全称:Internet Protocol version 6,互联网通信协议第六版

由于互联网的蓬勃发展,IP地址的需求量愈来愈大,而IPv4的模式下IP的总数是有限的。

采用128位地址长度,分为8组。

 2. 端口号

   应用程序在设备中唯一的标识。

端口号:

  • 由两个字节表示的整数,取值范围:0~65535。
  • 其中0~1023之间的端口号用于一些知名的网络服务或者应用。
  • 我们自己使用1024以上的端口号就可以。

注意:一个端口号只能被一个应用程序使用。

3. 协议

    在计算机网络中,连接和通信的规则被称为网络通信协议。

  • OS\参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广。
  • TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
    OSI参考模型 TCP/IP参考模型 TCP/IP参考模型各层对应协议 面向哪些
    应用层 应用层 HTTP、FTP、Telnet、DNS... 一把是应用程序需要关注的。如浏览器,邮箱。程序员一般在这一层开发
    表示层
    会话层
    传输层 传输层 TCP、UDP、... 选择传输使用的TCP,UDP协议
    网络层 网络层 lP、ICMP、ARP... 封装自己的IP,对方的IP等信息
    数据链路层
    物理+数据链路层

    硬件设备。

    010100101010100101010...

    转换成二进制利用物理设备传输
    物理层

3.1  UDP协议

  • 用户数据报协议(User Datagram Protocol)
  • UDP是面向无连接通信协议。速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。

3.2  TCP协议

  • 传输控制协议TCP(Transmission Control Protocol)
  • TCP协议是面向连接的通信协议。速度慢,没有大小限制,数据安全。

三、UDP通信程序

 1. 发送数据

  1. 创建发送端的DatagramSocket对象
  2. 数据打包(DatagramPacket
  3. 发送数据
  4. 释放资源
public class SendMessageDemo {
	public static void main(String[] args) throws IOException {
		// 1.创建DatagramSocket对象
		// 细节:
		// 绑定对应端口 后续通过端口发送数据
		// 空参:所有可用的端口中随机一个进行使用
		// 有参:指定端口号进行绑定
		DatagramSocket ds = new DatagramSocket();

		// 2.打包数据
		String str = "你好";
		byte[] bytes = str.getBytes();

		InetAddress address = InetAddress.getByName("127.0.0.1");
		int port = 10086;
		DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

		// 3.发送数据
		ds.send(dp);

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

2. 接收数据

  1. 创建接收端的DatagramSocket对象
  2. 接收打包好的数据
  3. 解析数据包
  4. 释放资源
public class ReceiveMessageDemo {
	public static void main(String[] args) throws IOException {
		// 1.创建DatagramSocket对象
		// 细节:
		// 在接收的时候,一定要绑定端口
		// 而且绑定的端口一定要与发送的端口保持一致
		DatagramSocket ds = new DatagramSocket();

		// 2.接收数据包
		byte[] bytes = new byte[1024];
		DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
		ds.receive(dp);

		// 3.解析数据包
		byte[] data = dp.getData();
		int len = dp.getLength();
		InetAddress address = dp.getAddress();
		int port = dp.getPort();

		System.out.println("接收到数据" + new String(data, 0, len));
		System.out.println("该数据是从" + address 
                     + "这台电脑中的" + port + "这个端口发出的");

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

3. UDP三种通信方式

  1. 单播:以前的代码就是单播。
  2. 组播:组播地址:224.0.0.0 ~ 239.255.255.255 (其中224.0.0.0 ~ 224.0.0.255 为预留的组播地址)
  3. 广播:广播地址:255.255.255.255

四、TCP通信程序

  • TCP通信协议是一种可靠的网络协议,它在通信两端各建立一个Socket对象,通信之前要保障连接已经建立。
  • 通过Socket产生IO流来进行网络通信。

代码书写步骤:
客户端 服务端
  1. 创建客户端的Socket对象(Socket)与指定服务端连接。
  2. 获取输出流,写数据。
  3. 释放资源
  1. 创建服务器端的Socket对象(ServerSocket)
  2. 监听客户端连接,返回Socket对象
  3. 获取输入流,读数据,并把数据显示在控制台上
  4. 释放资源
//Client.java
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
	//TCP协议 发送数据
	
	//1.创建客户端的Socket对象
	//细节:在创建对象同时会连接服务端,如果连不上代码会报错
	Socket sc = new Socket("127.0.0.1",10000);
	
	//2.获取输出流,写数据
	OutputStream os = sc.getOutputStream();
	os.write("你好".getBytes());
	
	//3.释放资源
	os.close();
	sc.close();
}
}

//Server.java
public class Server {
	public static void main(String[] args) throws IOException {
		// TCP协议 接收对象
		
		//1.创建服务器端的ServerSocket对象
		ServerSocket ss = new ServerSocket(10000);
		
		//2.监听客户端连接,返回Socket对象
		Socket sc = ss.accept();
		
		//3.获取输入流,读数据,并把数据显示在控制台上
		InputStream is = sc.getInputStream();
		int b;
		while((b = is.read()) != -1) {
			System.out.println((char)b);
		}
		
		//4.释放资源
		sc.close();
		ss.close();
	}
}

4.1  中文乱码问题

  os.write("你好你好".getBytes()); //12字节

  InputStream is = sc.getInputStream();
  int b;
  while((b = is.read()) != -1) {
	 System.out.println((char)b);
 }  //一个一个字节的读

所以我们要将字节流换成字符流

        InputStream is = sc.getInputStream();
		InputStreamReader isr = new InputStreamReader(is);
		BufferedReader br = new BufferedReader(isr);

 4.2  三次握手和四次挥手

三次握手 四次挥手
确保连接建立 确保连接断开,且数据处理完毕

 五、综合练习

1.  多收多发

客户端:多次发送数据
服务器:接收多次接收数据,并打印

public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//1.创建Socket对象,并连接服务端
		Socket socket = new Socket("127.0.0.1",10000);
		
		//2.写出数据
		Scanner sc = new Scanner(System.in);
		OutputStream os = socket.getOutputStream();
		while(true) {
			System.out.println("请输入您要输入的数据:");
			String str  = sc.nextLine();
			if ("n".equals(str)) {
				break;
			}
			os.write(str.getBytes());
		}
		
		//3.释放资源
		socket.close();
	}
}

public class Server {
	public static void main(String[] args) throws IOException {
		//1.创建ServerSocket对象指定端口
		ServerSocket ss = new ServerSocket(10000);
		
		//2.等待客户端连接
		Socket socket = ss.accept();
		
		//3.读取数据
		InputStreamReader isr= new InputStreamReader(socket.getInputStream());
		int b;
		while((b = isr.read()) !=-1) {
			System.out.println((char)b);
		}
		
		//4.释放资源
		socket.close();
        ss.close();
	}
}

2.  接收和反馈

客户端:发送一条数据,接收服务端反馈的消息并打印

服务器:接收数据并打印,再给客户端反馈消息

public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//1.创建Socket对象,并连接服务端
		Socket socket = new Socket("127.0.0.1",10000);
		
		//2.写出数据
		String str = "见到你很高兴!"; 
		OutputStream os = socket.getOutputStream();
		os.write(str.getBytes());
		
		//写一个结束标记
		socket.shutdownOutput();
		
		//3.接收服务端回写数据
		InputStream is = socket.getInputStream();
		InputStreamReader isr = new InputStreamReader(is);
		int b;
		while((b = isr.read()) != -1) {
			System.out.println((char)b);
		}
		
		//4.释放资源
		socket.close();
	}
}

public class Server {
	public static void main(String[] args) throws IOException {
		//1.创建ServerSocket对象指定端口
		ServerSocket ss = new ServerSocket(10000);
		
		//2.等待客户端连接
		System.out.println("等待客户端连接");
		Socket socket = ss.accept();
		System.out.println("连接成功");
		
		//3.读取数据
		InputStreamReader isr= new InputStreamReader(socket.getInputStream());
		int b;
		System.out.println("准备读取客户端发送过来的数据");
		//细节:
		//read方法会从连接通道中读取数据
		//但是,需要一个结束标记,此处的循环才会停止
		//否则,程序会一直停在read方法这,等待读取下面的数据
		while((b = isr.read()) !=-1) {
			System.out.println("正在读取数据");
			System.out.println((char)b);
		}
		
		System.out.println("数据读取完毕,准备回写");
		//4.回写数据
		String str = "到底有多开心呢?";
		OutputStream os	= socket.getOutputStream();
		os.write(str.getBytes());
		System.out.println("回写成功");
		
		//5.释放资源
		socket.close();
		ss.close();
	}
}

3.  上传文件

客户端:将本地文件上传到服务器。接收服务器的反馈。

服务器:接收客户端上传的文件,上传完毕之后给出反馈。

public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		// 1.创建Socket对象,并连接服务端
		Socket socket = new Socket("127.0.0.1", 10000);

		// 2.读取本地文件中的数据,并写到服务端中
		BufferedInputStream bis = new BufferedInputStream
                       (new FileInputStream("java02//a.png"));
		BufferedOutputStream bos = new 
                BufferedOutputStream(socket.getOutputStream());
		byte[] bytes = new byte[1024];
		int len;
		while ((len = bis.read()) != -1) {
			bos.write(bytes, 0, len);
		}
		//结束标记
		socket.shutdownOutput();

		// 3.接收服务端回写数据
		BufferedReader br = new BufferedReader
                 (new InputStreamReader(socket.getInputStream()));
		String line = br.readLine();
		System.out.println(line);

		// 4.释放资源
		socket.close();
	}
}


public class Server {
	public static void main(String[] args) throws IOException {
		//1.创建ServerSocket对象指定端口
		ServerSocket ss = new ServerSocket(10000);
		
		//2.等待客户端连接
		Socket socket = ss.accept();
		
		//3.读取数据并保存到本地文件
		BufferedInputStream bis = new BufferedInputStream
                      (socket.getInputStream());
		BufferedOutputStream bos = new BufferedOutputStream
                      (new FileOutputStream("java02\\b.png"));
		int len;
		byte[] bytes = new byte[1024];
		while ((len = bis.read()) != -1) {
			bos.write(bytes, 0, len);
		}
		
		//4.回写数据
		BufferedWriter bw = new BufferedWriter
                       (new OutputStreamWriter(socket.getOutputStream()));
		bw.write("上传成功");
		bw.newLine();
		bw.flush();
		
		//5.释放资源
		socket.close();
		ss.close();
	}
}

4.  上传文件(文件名重复问题)

解决上一题文件名重复问题。

解决方法:

//Server.java         
        BufferedInputStream bis = 
                  new BufferedInputStream(socket.getInputStream());
		String name = UUID.randomUUID().toString().replace("-", "");
		BufferedOutputStream bos = 
     
        new BufferedOutputStream(new FileOutputStream("java02\\"+name+".png"));

public class UUIDTest {
public static void main(String[] args) {
	String str = UUID.randomUUID().toString().replace("-", "");
	System.out.println(str);
}
}

5.  上传文件(多线程版)

想要服务器不停止,能接收很多用户上传的图片,该怎么做呢?
提示:可以用循环或者多线程。
但是循环不合理,最优解法是(循环+多线程)改写。

public class Server {
	public static void main(String[] args) throws IOException {
		// 1.创建ServerSocket对象指定端口
		ServerSocket ss = new ServerSocket(10000);

		while (true) {
			// 2.等待客户端连接
			Socket socket = ss.accept();

			//开启线程
			new Thread(new MyRunnable(socket)).start();
		}
	}
}


public class MyRunnable implements Runnable {

	Socket socket;

	public MyRunnable(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		try {
			// 3.读取数据并保存到本地文件
			BufferedInputStream bis = new 
                    BufferedInputStream(socket.getInputStream());
			String name = UUID.randomUUID().toString().replace("-", "");
			BufferedOutputStream bos = new BufferedOutputStream
                    (new FileOutputStream("java02\\" + name + ".png"));
			int len;
			byte[] bytes = new byte[1024];
			while ((len = bis.read()) != -1) {
				bos.write(bytes, 0, len);
			}

			// 4.回写数据
			BufferedWriter bw = new BufferedWriter
                      (new OutputStreamWriter(socket.getOutputStream()));
			bw.write("上传成功");
			bw.newLine();
			bw.flush();

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 5.释放资源
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

6.  上传文件(线程池优化)

频繁创建线程并销毁非常浪费系统资源,所以需要用线程池优化

public class Server {
	public static void main(String[] args) throws IOException {
		// 创建线程池对象
		ThreadPoolExecutor pool = new ThreadPoolExecutor(3, // 核心线程池
				16, // 线程池总大小
				60, // 空闲时间
				TimeUnit.SECONDS, // 空闲时间(单位)
				new ArrayBlockingQueue<>(2), // 队列
				Executors.defaultThreadFactory(), // 线程工厂
				new ThreadPoolExecutor.AbortPolicy() // 堵塞队列
		);

		// 1.创建ServerSocket对象指定端口
		ServerSocket ss = new ServerSocket(10000);

		while (true) {
			// 2.等待客户端连接
			Socket socket = ss.accept();

			// 开启线程
			// new Thread(new MyRunnable(socket)).start();
			pool.submit(new MyRunnable(socket));
		}
	}
}

猜你喜欢

转载自blog.csdn.net/hdakj22/article/details/129655998