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这一静态方法获取到实例对象的:

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

结果显示:解析成功!

// 控制台中的解析结果如下:
/192.168.153.1
DESKTOP-LEPR355/192.168.153.1
DESKTOP-LEPR355
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编程编写一个简易聊天程序

信息发送端代码:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner; /*
使用UDP编程模拟一个简易聊天程序,发送端输入"bye"退出程序
使用同一台主机的两个不同的端口模拟两台不同的主机
*/
public class DatagramSocketSender {
public static void main(String[] args) throws Exception {
//新建一个DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//新建一个扫描器
Scanner sc = new Scanner(System.in);
while(true){
//接收用户键入信息
System.out.println("Please input your message: ");
String line = sc.nextLine();
//判断用户输入的是不是"bye"
if(line.equals("bye")){
break;
}else{
//获取输入信息字符串的字节数组对象
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("localhost"), 8888);
ds.send(dp);
}
}
ds.close();
}
}

信息接收端代码:

import java.net.DatagramPacket;
import java.net.DatagramSocket; /*
模拟简易聊天程序的接收端,一般情况下,接收端看成是服务器,是不会轻易关闭的
*/
public class DatagramSocketReceiver {
public static void main(String[] args) throws Exception {
//绑定端口
DatagramSocket ds = new DatagramSocket(8888);
byte[] bys = new byte[64 * 1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
//服务端一般来说不会关闭
while(true){
//接收来自发送端的数据
ds.receive(dp);
//进行解析
byte[] data = dp.getData();
int len = dp.getLength();
System.out.println(new String(data,0,len));
}
}
}

使用UDP编程发送文件

文件发送端代码:

import java.io.BufferedReader;
import java.io.FileReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress; /*
使用UDP编程模拟文件的发送端
需要注意的是,因为要让文件接收端知道什么时候文件发送完成,需要写一个结束标记符
*/
public class FileSender {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("e:/test/a.txt"));
DatagramSocket ds = new DatagramSocket();
String line = null;
while((line = br.readLine()) != null){
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("localhost"), 8888);
ds.send(dp);
}
//文件已经全部发送完,最后发送一个结束标记,"886"
byte[] bys = "886".getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("localhost"), 8888);
ds.send(dp);
//关闭资源
br.close();
ds.close();
}
}

文件接收端代码:

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket; /*
使用UDP编程模拟文件接收方,当接收到字符"886"时表示文件已经发送完成,退出
*/
public class FileReceiver {
public static void main(String[] args) throws Exception {
BufferedWriter bw = new BufferedWriter(new FileWriter("e:/test/aa.txt"));
//指定端口
DatagramSocket ds = new DatagramSocket(8888);
//封装数据包
byte[] bys = new byte[64 * 1024];
DatagramPacket dp = new DatagramPacket(bys,bys.length);
//解析接收到的数据并将其写出到另一个文件中去
while(true){
//接收数据
ds.receive(dp);
byte[] data = dp.getData();
int len = dp.getLength();
String line = new String(data,0,len);
if(line.equals("886")){
break;
}else{
bw.write(line);
bw.newLine();
bw.flush();
}
}
//关闭资源
bw.close();
ds.close();
}
}

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

图片发送端代码

import java.io.File;
import java.io.RandomAccessFile;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress; /*
UDP编程模拟发送一张图片
*/
public class PictureSender {
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket();
//使用RandomAccessFile,因为可以移动指针
RandomAccessFile raf = new RandomAccessFile("e:/test/picture.png", "rw");
//新建一个字节数组进行数据拷贝,大小应小于64k,因为要存放一些控制字符
int size = 60 * 1024;
byte[] bys = new byte[size];
int fileLen = (int) new File("e:/test/picture.png").length();
int count = fileLen % size == 0 ? fileLen / size : fileLen / size + 1;
for(int i = 0; i < count; i++){
int len = size;
if(fileLen % size != 0 && i == count - 1){
len = fileLen % size;
}
raf.seek(i * size);
byte[] buf = new byte[len];
raf.read(buf);
//封装数据包
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("localhost"), 8888);
ds.send(dp);
}
raf.close();
ds.close();
}
}

图片接收端代码

import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket; /*
使用UDP编程模拟图片接收端
*/
public class PictureReceiver {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("e:/test/picture1.png");
//指定端口
DatagramSocket ds = new DatagramSocket(8888);
byte[] bys = new byte[64 * 1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
while(true){
ds.receive(dp);
byte[] data = dp.getData();
int len = dp.getLength();
fos.write(data,0,len);
}
}
}

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编程实战:将一个本地文件发送到本机的另一个端口上去

客户端:

import java.io.*;
import java.net.Socket; /*
使用TCP编程模拟将一个文件传输到另一个端口上的客户端
*/
public class TCPClient {
public static void main(String[] args) throws Exception {
//新建一个Socket对象,指定需要发送到的IP地址和端口
Socket socket = new Socket("localhost", 8888);
//新建一个输入流读取本地文件
BufferedReader br = new BufferedReader(new FileReader("e:/test/a.txt"));
//通过Socket对象新建一个输出流,并用转换流进行包装
OutputStream os = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
//开始按行读取并写出
String line = null;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
bw.close();
os.close();
br.close();
socket.close();
}
}

服务端:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket; /*
TCP编程模拟服务端接收文件
*/
public class TCPServer {
public static void main(String[] args) throws Exception {
//绑定端口
ServerSocket ss = new ServerSocket(8888);
//调用accept方法接收数据,是一个阻塞式方法
Socket socket = ss.accept();
//使用ss创建输入流
InputStream is = socket.getInputStream();
//进行包装
BufferedReader br = new BufferedReader(new InputStreamReader(is));
BufferedWriter bw = new BufferedWriter(new FileWriter("e:/test/aa.txt"));
String line = null;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
is.close();
br.close();
bw.close();
socket.close();
ss.close();
}
}

1.5.3 流的半关闭

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

客户端:

import java.io.*;
import java.net.Socket; /*
使用TCP编程模拟将一个文件传输到另一个端口上的客户端
改进:并接收来自服务端的反馈消息
*/
public class TCPClient2 {
public static void main(String[] args) throws Exception {
//新建一个Socket对象,指定需要发送到的IP地址和端口
Socket socket = new Socket("localhost", 8888);
//新建一个输入流读取本地文件
BufferedReader br = new BufferedReader(new FileReader("e:/test/a.txt"));
//通过Socket对象新建一个输出流,并用转换流进行包装
OutputStream os = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
//开始按行读取并写出
String line = null;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
//将流半关闭
socket.shutdownOutput();
//新建一个输入流来接收来自服务端的反馈消息
InputStream is = socket.getInputStream();
byte[] buf = new byte[1024];
int len = is.read(buf);
System.out.println(new String(buf,0,len));
//关闭资源
is.close();
bw.close();
os.close();
br.close();
socket.close();
}
}

服务端:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket; /*
TCP编程模拟服务端接收文件
改进:并给客户端发送反馈消息
*/
public class TCPServer2 {
public static void main(String[] args) throws Exception {
//绑定端口
ServerSocket ss = new ServerSocket(8888);
//调用accept方法接收数据,是一个阻塞式方法
Socket socket = ss.accept();
//使用ss创建输入流
InputStream is = socket.getInputStream();
//进行包装
BufferedReader br = new BufferedReader(new InputStreamReader(is));
BufferedWriter bw = new BufferedWriter(new FileWriter("e:/test/aa.txt"));
String line = null;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
} //接收完文件之后,再新建一个输出流告诉客户端文件接收完毕
OutputStream os = socket.getOutputStream();
os.write("文件接收完毕!".getBytes());
//关闭资源
os.close();
is.close();
br.close();
bw.close();
socket.close();
ss.close();
}
}

如果在客户端代码中不添加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. nyoj 739 笨蛋难题四

    笨蛋难题四 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 这些日子笨蛋一直研究股票,经过调研,终于发现xxx公司股票规律,更可喜的是 笨蛋推算出这家公司每天的股价, ...

  2. python:模块1——标准库简介

    一.文档 windows系统:IDLE中打开帮助文档 Tutorial:简单入门 Library Reference:python内置函数和标准库(看不完的,当做字典来查)(此外还有pypi(拍派社区 ...

  3. Vue项目使用CSS变量实现主题化

    主题化管理经常能在网站上看到,一般的思路都是将主题相关的CSS样式独立出来,在用户选择主题的时候加载相应的CSS样式文件.现在大部分浏览器都能很好的兼容CSS变量,主题化样式更容易管理了.最近,使用C ...

  4. (五)OpenStack---M版---双节点搭建---Nova安装和配置

    ↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ >>>>>>传送门 1.创建nova 和 nova_api数据库 2.获得 admin 凭证来获取只有管理员能执行的 ...

  5. scikit-learn_cookbook1: 高性能机器学习-NumPy

    源码下载 在本章主要内容: NumPy基础知识 加载iris数据集 查看iris数据集 用pandas查看iris数据集 用NumPy和matplotlib绘图 最小机器学习配方 - SVM分类 介绍 ...

  6. linux终端操作

    ------------恢复内容开始------------ tab键自动补全 ls列出当前文件目录: 默认是当前目录 “.”代表当前目录 “..”代表父目录 -a显示所有,而隐藏文件的第一字符为点“ ...

  7. 高性能消息队列(MQ)Kafka 简单由来介绍(1)

    Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写.Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据. 这种动作(网页浏 ...

  8. C语言基础——进制转换 / 数据表示

    第一部分:进制转换 二进制:由0~1构成,逢2进1 八进制:由0~7构成,逢8进1 十六进制:由0~9.A~F构成,逢16进1 两个基本概念 基数:n进制基数为n 123.4 = 1*10^2 + 2 ...

  9. Spring Security(二)--WebSecurityConfigurer配置以及filter顺序

    “致"高级"工程师(BUG工程师) 一颗折腾的心

  10. Linux的awk命令详解

    awkawk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,默认以空格为分隔符将每行切片,切开的部分再 ...