大数据学习笔记——Java篇之网络编程基础
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篇之网络编程基础的更多相关文章
- 大数据学习笔记——Java篇之集合框架(ArrayList)
Java集合框架学习笔记 1. Java集合框架中各接口或子类的继承以及实现关系图: 2. 数组和集合类的区别整理: 数组: 1. 长度是固定的 2. 既可以存放基本数据类型又可以存放引用数据类型 3 ...
- 大数据学习笔记——Java篇之IO
IO学习笔记整理 1. File类 1.1 File对象的三种创建方式: File对象是一个抽象的概念,只有被创建出来之后,文件或文件夹才会真正存在 注意:File对象想要创建成功,它的目录必须存在! ...
- 大数据学习笔记——Java篇之基础知识
Java / 计算机基础知识整理 在进行知识梳理同时也是个人的第一篇技术博客之前,首先祝贺一下,经历了一年左右的学习,从完完全全的计算机小白,现在终于可以做一些产出了!可以说也是颇为感慨,个人认为,学 ...
- javaSE学习笔记(16)---网络编程
javaSE学习笔记(16)---网络编程 基本概念 如今,计算机已经成为人们学习.工作.生活必不可少的工具.我们利用计算机可以和亲朋好友网上聊天,也可以玩网游.发邮件等等,这些功能实现都离不开计算机 ...
- 大数据学习笔记——Linux完整部署篇(实操部分)
Linux环境搭建完整操作流程(包含mysql的安装步骤) 从现在开始,就正式进入到大数据学习的前置工作了,即Linux的学习以及安装,作为运行大数据框架的基础环境,Linux操作系统的重要性自然不言 ...
- 大数据学习笔记之Hadoop(一):Hadoop入门
文章目录 大数据概论 一.大数据概念 二.大数据的特点 三.大数据能干啥? 四.大数据发展前景 五.企业数据部的业务流程分析 六.企业数据部的一般组织结构 Hadoop(入门) 一 从Hadoop框架 ...
- 大数据学习笔记——Hadoop编程实战之HDFS
HDFS基本API的应用(包含IDEA的基本设置) 在上一篇博客中,本人详细地整理了如何从0搭建一个HA模式下的分布式Hadoop平台,那么,在上一篇的基础上,我们终于可以进行编程实操了,同样,在编程 ...
- 大数据学习笔记——Linux基本知识及指令(理论部分)
Linux学习笔记整理 上一篇博客中,我们详细地整理了如何从0部署一套Linux操作系统,那么这一篇就承接上篇文章,我们仔细地把Linux的一些基础知识以及常用指令(包括一小部分高级命令)做一个梳理, ...
- 大数据学习笔记——Hadoop编程实战之Mapreduce
Hadoop编程实战——Mapreduce基本功能实现 此篇博客承接上一篇总结的HDFS编程实战,将会详细地对mapreduce的各种数据分析功能进行一个整理,由于实际工作中并不会过多地涉及原理,因此 ...
随机推荐
- hdu 5901 Count primes (meisell-Lehmer)
Count primes Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Tot ...
- Ubuntu 16.04安装ROS Kinetic详细教程 | Tutorial to Install and Configure ROS Kinetic on Ubuntu 16.04
本文首发于个人博客https://kezunlin.me/post/e2780b93/,欢迎阅读! Tutorial to Install and Configure ROS Kinetic on U ...
- 全栈项目|小书架|微信小程序-登录及token鉴权
小程序登录 之前也写过微信小程序登录的相关文章: 微信小程序~新版授权用户登录例子 微信小程序-携带Token无感知登录的网络请求方案 微信小程序开通云开发并利用云函数获取Openid 也可以通过官方 ...
- 2019-10-8:渗透测试,基础学习,php基础,会话,文件包含,笔记
php面向对象基础->调用符号构造函数construct,主要用来创建对象时初始化对象,为成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 析构函数destructor,与构造函数相 ...
- Jenkins修改默认主目录及数据迁移
前言 在使用Jenkins做持续集成的初期,未能预估项目量的大小.于是乎,配置都是使用的默认配置,而Jenkins的默认主目录放在了服务器的根目录下. 随着时间的推移,项目量的持续增加,在运维过程中就 ...
- YoungLots Team - Record a software installation
一.写在最前 本文记录安装或配置以下软件或环境的过程:VScode,Xampp,navicat,PHP,html,CSS,SQL,JavaScript. 作者使用的环境:浏览器:Google Chro ...
- 【Android - 自定义View】之View的draw过程解析
draw(绘制)过程的作用是将View绘制到屏幕上面.View中有 draw() 方法和 onDraw() 方法,但onDraw()方法是空方法:ViewGroup中没有draw()方法,也没有onD ...
- Objective-C (OC)
1.OC 运行效率比较高,仅次于c.开发过程时,效率也比较高,不会次于java和C++: :表示继承 “{}”之间写字段或声明 }和@end之间写函数 import 相当于include 作用于和j ...
- APM环境搭建
权限设置 把用户添加到用户组 “dialout”(如果这步没做,会导致很多用户权限问题): sudo usermod -a -G dialout $USER 然后注销后,重新登录,因为重新登录后所 ...
- libnl的移植
libnl简介 libnl是为了方便应用程序使用netlink接口而开发的一个库.这个库为原始netlink消息传递以及不同的netlink,family专用接口提供了一个统一的接口.libnl2.0 ...