Java网络编程学习笔记

1. 网络编程基础知识

1.1 网络分层图

网络分层分为两种模型:OSI模型以及TCP/IP网络模型,前者模型分为7层,是一个理论的,参考的模型;后者为实际应用的模型,具体对应关系见下图:

1.2 网络编程三要素之IP地址

目前的版本是ipv4,采用的是点分十进制的表示方式(dotted-decimal notation),一共4位,每一位表示一个字节,因为IP地址是没有负数的,因此表数范围是0-255,子网掩码的存在使得一个IP地址被区分为了两个部分,网络地址以及主机地址,原理如下:比如一个子网掩码为255.255.255.0,实际上相当于将整个地址的前三位划分成了网络地址,而最后一位划分成了主机地址,网络地址越少,主机位越多,可分配的主机数量就越多,IP地址的分类图如下:

一般企业使用的是C类网址,校园网使用的是B类网址,而A类网址往往是政府,国家使用

1.3 IP地址编程实战——InetAddress类

查看该类的API可知,它的构造方法是私有的,因此无法通过new的方式创建对象,而是通过getByName这一静态方法获取到实例对象的:

  1. /*
  2. 演示InetAddress类的基本API
  3. */
  4. public class InetAddressDemo {
  5. public static void main(String[] args) throws Exception {
  6. //1.通过给定IP地址的方式获取到InetAddress对象
  7. InetAddress ip1 = InetAddress.getByName("192.168.153.1");
  8. System.out.println(ip1);
  9. //2.通过给定主机名的方式获取到InetAddress对象
  10. InetAddress ip2 = InetAddress.getByName("DESKTOP-LEPR355");
  11. System.out.println(ip2);
  12. //3.通过获取到的InetAddress解析出主机名
  13. System.out.println(ip1.getHostName());
  14. //4.通过获取到的InetAddress解析出主机的IP地址
  15. System.out.println(ip2.getHostAddress());
  16.  
  17. }
  18. }

结果显示:解析成功!

  1. // 控制台中的解析结果如下:
  2. /192.168.153.1
  3. DESKTOP-LEPR355/192.168.153.1
  4. DESKTOP-LEPR355
  5. 192.168.153.1

1.4 网络编程三要素之端口(port)

端口号是用来唯一表示计算机上运行着的进程的,以一个数字进行指定,该数字的范围是0-65535共65536个,其中系统进程使用的是0-1023,因此应用程序只能使用后面的数字,否则会发生冲突,使得应用程序运行失败

1.5 网络编程三要素之协议(Protocal)

两个主机之间想要相互通信,必须要使用相同的语言,对于数据的格式,错误控制,重传机制等的一系列规范就是协议

1.5.1 UDP编程

UDP是User Datagram Protocal的缩写:用户数据报协议,它的特点是不用建立连接,发送端只管发送数据给接收端而不必考虑接收端是否真的接收到了数据,数据是被封装成数据包的,限制在64K,数据包自己知道它应该去哪一个主机的哪一个端口,因为UDP编程的这一特性,使得它的发送效率高,速度快,通常可以用在发送短信或是网络电话,网络会议等场景上,发送端和接收端的编程步骤如下所示:

其中receive方法是一个阻塞式方法,没有信息发送过来,此方法就会一直处于等待状态

使用同一台主机上的两个端口模拟UDP编程编写一个简易聊天程序

信息发送端代码:

  1. import java.net.DatagramPacket;
  2. import java.net.DatagramSocket;
  3. import java.net.InetAddress;
  4. import java.util.Scanner;
  5.  
  6. /*
  7. 使用UDP编程模拟一个简易聊天程序,发送端输入"bye"退出程序
  8. 使用同一台主机的两个不同的端口模拟两台不同的主机
  9. */
  10. public class DatagramSocketSender {
  11. public static void main(String[] args) throws Exception {
  12. //新建一个DatagramSocket对象
  13. DatagramSocket ds = new DatagramSocket();
  14. //新建一个扫描器
  15. Scanner sc = new Scanner(System.in);
  16. while(true){
  17. //接收用户键入信息
  18. System.out.println("Please input your message: ");
  19. String line = sc.nextLine();
  20. //判断用户输入的是不是"bye"
  21. if(line.equals("bye")){
  22. break;
  23. }else{
  24. //获取输入信息字符串的字节数组对象
  25. byte[] bys = line.getBytes();
  26. DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("localhost"), 8888);
  27. ds.send(dp);
  28. }
  29. }
  30. ds.close();
  31. }
  32. }

信息接收端代码:

  1. import java.net.DatagramPacket;
  2. import java.net.DatagramSocket;
  3.  
  4. /*
  5. 模拟简易聊天程序的接收端,一般情况下,接收端看成是服务器,是不会轻易关闭的
  6. */
  7. public class DatagramSocketReceiver {
  8. public static void main(String[] args) throws Exception {
  9. //绑定端口
  10. DatagramSocket ds = new DatagramSocket(8888);
  11. byte[] bys = new byte[64 * 1024];
  12. DatagramPacket dp = new DatagramPacket(bys, bys.length);
  13. //服务端一般来说不会关闭
  14. while(true){
  15. //接收来自发送端的数据
  16. ds.receive(dp);
  17. //进行解析
  18. byte[] data = dp.getData();
  19. int len = dp.getLength();
  20. System.out.println(new String(data,0,len));
  21. }
  22. }
  23. }

使用UDP编程发送文件

文件发送端代码:

  1. import java.io.BufferedReader;
  2. import java.io.FileReader;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. import java.net.InetAddress;
  6.  
  7. /*
  8. 使用UDP编程模拟文件的发送端
  9. 需要注意的是,因为要让文件接收端知道什么时候文件发送完成,需要写一个结束标记符
  10. */
  11. public class FileSender {
  12. public static void main(String[] args) throws Exception {
  13. BufferedReader br = new BufferedReader(new FileReader("e:/test/a.txt"));
  14. DatagramSocket ds = new DatagramSocket();
  15. String line = null;
  16. while((line = br.readLine()) != null){
  17. byte[] bys = line.getBytes();
  18. DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("localhost"), 8888);
  19. ds.send(dp);
  20. }
  21. //文件已经全部发送完,最后发送一个结束标记,"886"
  22. byte[] bys = "886".getBytes();
  23. DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("localhost"), 8888);
  24. ds.send(dp);
  25. //关闭资源
  26. br.close();
  27. ds.close();
  28. }
  29. }

文件接收端代码:

  1. import java.io.BufferedWriter;
  2. import java.io.FileWriter;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5.  
  6. /*
  7. 使用UDP编程模拟文件接收方,当接收到字符"886"时表示文件已经发送完成,退出
  8. */
  9. public class FileReceiver {
  10. public static void main(String[] args) throws Exception {
  11. BufferedWriter bw = new BufferedWriter(new FileWriter("e:/test/aa.txt"));
  12. //指定端口
  13. DatagramSocket ds = new DatagramSocket(8888);
  14. //封装数据包
  15. byte[] bys = new byte[64 * 1024];
  16. DatagramPacket dp = new DatagramPacket(bys,bys.length);
  17. //解析接收到的数据并将其写出到另一个文件中去
  18. while(true){
  19. //接收数据
  20. ds.receive(dp);
  21. byte[] data = dp.getData();
  22. int len = dp.getLength();
  23. String line = new String(data,0,len);
  24. if(line.equals("886")){
  25. break;
  26. }else{
  27. bw.write(line);
  28. bw.newLine();
  29. bw.flush();
  30. }
  31. }
  32. //关闭资源
  33. bw.close();
  34. ds.close();
  35. }
  36. }

上述代码是对文本文件进行操作,因为是按行进行读取,因此可以将文件的每一行数据封装成一个数据包,但是,如果操作的是图片,视频,mp3等非文本文件,就需要对代码进行更改了

图片发送端代码

  1. import java.io.File;
  2. import java.io.RandomAccessFile;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. import java.net.InetAddress;
  6.  
  7. /*
  8. UDP编程模拟发送一张图片
  9. */
  10. public class PictureSender {
  11. public static void main(String[] args) throws Exception {
  12. DatagramSocket ds = new DatagramSocket();
  13. //使用RandomAccessFile,因为可以移动指针
  14. RandomAccessFile raf = new RandomAccessFile("e:/test/picture.png", "rw");
  15. //新建一个字节数组进行数据拷贝,大小应小于64k,因为要存放一些控制字符
  16. int size = 60 * 1024;
  17. byte[] bys = new byte[size];
  18. int fileLen = (int) new File("e:/test/picture.png").length();
  19. int count = fileLen % size == 0 ? fileLen / size : fileLen / size + 1;
  20. for(int i = 0; i < count; i++){
  21. int len = size;
  22. if(fileLen % size != 0 && i == count - 1){
  23. len = fileLen % size;
  24. }
  25. raf.seek(i * size);
  26. byte[] buf = new byte[len];
  27. raf.read(buf);
  28. //封装数据包
  29. DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("localhost"), 8888);
  30. ds.send(dp);
  31. }
  32. raf.close();
  33. ds.close();
  34. }
  35. }

图片接收端代码

  1. import java.io.FileOutputStream;
  2. import java.net.DatagramPacket;
  3. import java.net.DatagramSocket;
  4.  
  5. /*
  6. 使用UDP编程模拟图片接收端
  7. */
  8. public class PictureReceiver {
  9. public static void main(String[] args) throws Exception {
  10. FileOutputStream fos = new FileOutputStream("e:/test/picture1.png");
  11. //指定端口
  12. DatagramSocket ds = new DatagramSocket(8888);
  13. byte[] bys = new byte[64 * 1024];
  14. DatagramPacket dp = new DatagramPacket(bys, bys.length);
  15. while(true){
  16. ds.receive(dp);
  17. byte[] data = dp.getData();
  18. int len = dp.getLength();
  19. fos.write(data,0,len);
  20. }
  21. }
  22. }

1.5.2 TCP编程

TCP是Transmission Control Protocal的缩写,俗称的TCP/IP中,IP协议用于唯一标识两个主机,而TCP协议用于确保传输完整可靠,它在网络两端各建立一个Socket,从而在通信的两端形成一个虚拟的网络连接,一旦连接建立,就可以在两端进行通信了,而Socket网络通信底层使用的是IO技术

UDP编程与TCP编程的几个区别总结:

1. UDP没有客户端,服务端之分,只有发送端和接收端,两端之间无需建立连接,而在TCP编程中明确地对于客户端和服务端进行了区分

2. 如果客户端指定的IP地址不存在,就会报UnknownHostException异常;而如果是端口号不存在,那么就会报Connection refused异常,在客户端创建套接字对象时需要明确指定两者,不可缺其一,而UDP编程并不会

3. 在UDP编程中,需要给接收端发送一个结束标记让接收端知道何时应该退出循环,而TCP编程由于底层使用的仍然是IO,因此只需要判断是否读到-1或是null就可以了

TCP编程实战:将一个本地文件发送到本机的另一个端口上去

客户端:

  1. import java.io.*;
  2. import java.net.Socket;
  3.  
  4. /*
  5. 使用TCP编程模拟将一个文件传输到另一个端口上的客户端
  6. */
  7. public class TCPClient {
  8. public static void main(String[] args) throws Exception {
  9. //新建一个Socket对象,指定需要发送到的IP地址和端口
  10. Socket socket = new Socket("localhost", 8888);
  11. //新建一个输入流读取本地文件
  12. BufferedReader br = new BufferedReader(new FileReader("e:/test/a.txt"));
  13. //通过Socket对象新建一个输出流,并用转换流进行包装
  14. OutputStream os = socket.getOutputStream();
  15. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
  16. //开始按行读取并写出
  17. String line = null;
  18. while((line = br.readLine()) != null){
  19. bw.write(line);
  20. bw.newLine();
  21. bw.flush();
  22. }
  23. //关闭资源
  24. bw.close();
  25. os.close();
  26. br.close();
  27. socket.close();
  28. }
  29. }

服务端:

  1. import java.io.*;
  2. import java.net.ServerSocket;
  3. import java.net.Socket;
  4.  
  5. /*
  6. TCP编程模拟服务端接收文件
  7. */
  8. public class TCPServer {
  9. public static void main(String[] args) throws Exception {
  10. //绑定端口
  11. ServerSocket ss = new ServerSocket(8888);
  12. //调用accept方法接收数据,是一个阻塞式方法
  13. Socket socket = ss.accept();
  14. //使用ss创建输入流
  15. InputStream is = socket.getInputStream();
  16. //进行包装
  17. BufferedReader br = new BufferedReader(new InputStreamReader(is));
  18. BufferedWriter bw = new BufferedWriter(new FileWriter("e:/test/aa.txt"));
  19. String line = null;
  20. while((line = br.readLine()) != null){
  21. bw.write(line);
  22. bw.newLine();
  23. bw.flush();
  24. }
  25. //关闭资源
  26. is.close();
  27. br.close();
  28. bw.close();
  29. socket.close();
  30. ss.close();
  31. }
  32. }

1.5.3 流的半关闭

场景说明:现在需要对上述代码进行改良,在服务端接收完来自客户端的数据之后,再给客户端发送一个反馈消息,告诉客户端数据接收完毕:

客户端:

  1. import java.io.*;
  2. import java.net.Socket;
  3.  
  4. /*
  5. 使用TCP编程模拟将一个文件传输到另一个端口上的客户端
  6. 改进:并接收来自服务端的反馈消息
  7. */
  8. public class TCPClient2 {
  9. public static void main(String[] args) throws Exception {
  10. //新建一个Socket对象,指定需要发送到的IP地址和端口
  11. Socket socket = new Socket("localhost", 8888);
  12. //新建一个输入流读取本地文件
  13. BufferedReader br = new BufferedReader(new FileReader("e:/test/a.txt"));
  14. //通过Socket对象新建一个输出流,并用转换流进行包装
  15. OutputStream os = socket.getOutputStream();
  16. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
  17. //开始按行读取并写出
  18. String line = null;
  19. while((line = br.readLine()) != null){
  20. bw.write(line);
  21. bw.newLine();
  22. bw.flush();
  23. }
  24. //将流半关闭
  25. socket.shutdownOutput();
  26. //新建一个输入流来接收来自服务端的反馈消息
  27. InputStream is = socket.getInputStream();
  28. byte[] buf = new byte[1024];
  29. int len = is.read(buf);
  30. System.out.println(new String(buf,0,len));
  31. //关闭资源
  32. is.close();
  33. bw.close();
  34. os.close();
  35. br.close();
  36. socket.close();
  37. }
  38. }

服务端:

  1. import java.io.*;
  2. import java.net.ServerSocket;
  3. import java.net.Socket;
  4.  
  5. /*
  6. TCP编程模拟服务端接收文件
  7. 改进:并给客户端发送反馈消息
  8. */
  9. public class TCPServer2 {
  10. public static void main(String[] args) throws Exception {
  11. //绑定端口
  12. ServerSocket ss = new ServerSocket(8888);
  13. //调用accept方法接收数据,是一个阻塞式方法
  14. Socket socket = ss.accept();
  15. //使用ss创建输入流
  16. InputStream is = socket.getInputStream();
  17. //进行包装
  18. BufferedReader br = new BufferedReader(new InputStreamReader(is));
  19. BufferedWriter bw = new BufferedWriter(new FileWriter("e:/test/aa.txt"));
  20. String line = null;
  21. while((line = br.readLine()) != null){
  22. bw.write(line);
  23. bw.newLine();
  24. bw.flush();
  25. }
  26.  
  27. //接收完文件之后,再新建一个输出流告诉客户端文件接收完毕
  28. OutputStream os = socket.getOutputStream();
  29. os.write("文件接收完毕!".getBytes());
  30. //关闭资源
  31. os.close();
  32. is.close();
  33. br.close();
  34. bw.close();
  35. socket.close();
  36. ss.close();
  37. }
  38. }

如果在客户端代码中不添加socket.shutdownOutput();这句代码,会发生两端相互等待的情况,这是因为:客户端将文件发送到了末尾,并不会通过流将最后的null传给服务端,因此进入到了等待状态,而服务端一直没有接收到null值,以为还有数据传送过来,在socket对象没有关闭之前就一直在循环等待接收文件,而shutdownOutput方法相当于在把socket关闭之前,先把由socket对象创建的输出流先关闭了,从而避免了相互等待的情况出现,因此这个又被称为是流的半关闭

大数据学习笔记——Java篇之网络编程基础的更多相关文章

  1. 大数据学习笔记——Java篇之集合框架(ArrayList)

    Java集合框架学习笔记 1. Java集合框架中各接口或子类的继承以及实现关系图: 2. 数组和集合类的区别整理: 数组: 1. 长度是固定的 2. 既可以存放基本数据类型又可以存放引用数据类型 3 ...

  2. 大数据学习笔记——Java篇之IO

    IO学习笔记整理 1. File类 1.1 File对象的三种创建方式: File对象是一个抽象的概念,只有被创建出来之后,文件或文件夹才会真正存在 注意:File对象想要创建成功,它的目录必须存在! ...

  3. 大数据学习笔记——Java篇之基础知识

    Java / 计算机基础知识整理 在进行知识梳理同时也是个人的第一篇技术博客之前,首先祝贺一下,经历了一年左右的学习,从完完全全的计算机小白,现在终于可以做一些产出了!可以说也是颇为感慨,个人认为,学 ...

  4. javaSE学习笔记(16)---网络编程

    javaSE学习笔记(16)---网络编程 基本概念 如今,计算机已经成为人们学习.工作.生活必不可少的工具.我们利用计算机可以和亲朋好友网上聊天,也可以玩网游.发邮件等等,这些功能实现都离不开计算机 ...

  5. 大数据学习笔记——Linux完整部署篇(实操部分)

    Linux环境搭建完整操作流程(包含mysql的安装步骤) 从现在开始,就正式进入到大数据学习的前置工作了,即Linux的学习以及安装,作为运行大数据框架的基础环境,Linux操作系统的重要性自然不言 ...

  6. 大数据学习笔记之Hadoop(一):Hadoop入门

    文章目录 大数据概论 一.大数据概念 二.大数据的特点 三.大数据能干啥? 四.大数据发展前景 五.企业数据部的业务流程分析 六.企业数据部的一般组织结构 Hadoop(入门) 一 从Hadoop框架 ...

  7. 大数据学习笔记——Hadoop编程实战之HDFS

    HDFS基本API的应用(包含IDEA的基本设置) 在上一篇博客中,本人详细地整理了如何从0搭建一个HA模式下的分布式Hadoop平台,那么,在上一篇的基础上,我们终于可以进行编程实操了,同样,在编程 ...

  8. 大数据学习笔记——Linux基本知识及指令(理论部分)

    Linux学习笔记整理 上一篇博客中,我们详细地整理了如何从0部署一套Linux操作系统,那么这一篇就承接上篇文章,我们仔细地把Linux的一些基础知识以及常用指令(包括一小部分高级命令)做一个梳理, ...

  9. 大数据学习笔记——Hadoop编程实战之Mapreduce

    Hadoop编程实战——Mapreduce基本功能实现 此篇博客承接上一篇总结的HDFS编程实战,将会详细地对mapreduce的各种数据分析功能进行一个整理,由于实际工作中并不会过多地涉及原理,因此 ...

随机推荐

  1. lqb 基础练习 字母图形 (循环)

    基础练习 字母图形 时间限制:1.0s   内存限制:256.0MB     问题描述 利用字母可以组成一些美丽的图形,下面给出了一个例子: ABCDEFG BABCDEF CBABCDE DCBAB ...

  2. pat 1136 A Delayed Palindrome(20 分)

    1136 A Delayed Palindrome(20 分) Consider a positive integer N written in standard notation with k+1 ...

  3. Python3.7.1学习(七)mysql中pymysql模块详解(一)

    pymysql是纯用Python操作MySQL的模块,其使用方法和MySQLdb几乎相同.此次介绍mysql以及在python中如何用pymysql操作数据库, 以及在mysql中存储过程, 触发器以 ...

  4. 力扣(LeetCode)移除链表元素 个人题解

    删除链表中等于给定值 val 的所有节点. 这题粗看并不困难,链表的特性让移除元素特别轻松,只用遇到和val相同的就跳过,将指针指向下一个,以此类推. 但是,一个比较麻烦的问题是,当链表所有元素都和v ...

  5. ArcGIS 问题汇总

    1.Arcgis10.4出现Manager打不开的情况 解决方法: 1.检查进程中是否有占用4000以及6080端口的进程,如果有关闭 2.检查进程中是否有javaw.exe这个进程,如果有就把他结束 ...

  6. 20191010-7 alpha week 1/2 Scrum立会报告+燃尽图 05

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/8750 一.小组情况 队名:扛把子 组长:迟俊文 组员:宋晓丽 梁梦瑶 韩 ...

  7. HTTP基础及telnet简单命令

    一.HTTP概况 20世纪90年代初期,一个主要的新兴应用即万维网(World Wide Web)登上了舞台.Web是一个引起公众注意的因特网应用.Web的应用层协议是超文本传输协议(HTTP),它是 ...

  8. 【Luogu P2563】【集训Day 4 动态规划】质数和分解

    题目链接:Luogu P2563 质数和分解(prime) [问题描述] 任何大于 1 的自然数 N,都可以写成若干个大于等于2且小于等于 N 的质数之和表达式(包括只有一个数构成的和表达式的情况), ...

  9. nginx离线部署脚本

    #! /bin/bashbasepath=$(cd `dirname $0`; pwd)nginx_path=/usr/localfile_name=nginxecho "--------- ...

  10. 关于vue中的videoPlayer的src视频地址参数动态修改(网上一堆错误方法,被误导很久,自己找到了正确的方法,供大家借鉴)

    方法很简单:相信大家的问题应该是改变src的值吧,动态赋值这一步简单.this.playerOptions['sources'][0]['src'] 就是这一步解决提取src问题,主要部分用绿色框起来 ...