网络编程
大家好呀!我是小笙!本节我和大家分享一下网络编程的基础知识!
网络编程
网络基础知识
概念
- 概念:两台或者多台设备之间通过网络实现数据传输
- Java.net包下提供了一系列的类和接口用来实现网络通信
- 网络的覆盖范围
- 局域网:一个机房或者教室
- 城域网:一个城市
- 广域网:全国甚至全球范围
IP地址
- 概念:用来标识每一台电脑主机 <=> 地址
- 查看ip地址:ipconfig
- 组成:网络地址+主机地址 如 192.168.16.22
IPv6的地址长度为128位,16个字节是IPv4的四倍
域名
概念:将ip地址映射成域名
目的:解决记忆ip地址困难的问题
www.baidu.com <=> 180.101.49.11
端口号
概念:用于标识特定的网络服务程序(注意是网络服务需要端口,普通程序并不一定需要占用端口)
范围:以整数形式 0~65535(2个字节) (注意0~1024通常被占用)
网络通讯协议
数据,在网络编程中,数据的组织形式就是协议(需要按照规定好的协议方式)
TCP 和 UDP
InetAddress
实现功能
方法:(InetAddress对象: 主机名/IP地址)
- 获取本机的InetAddress对象 — localHost
- 根据指定主机名获取InetAddress对象 — InetAddress.getByName(“主机名”)
- 根据域名返回InetAddress对象 — InetAddress.getByName(“域名”)
- 通过InetAddress对象,获取对应的IP地址 — InetAddress对象.getHostAddress()
- 通过InetAddress对象 获取对应的主机名或者域名 — InetAddress对象.getByName(“主机名”)
操作代码示例
需要捕获异常,可能会出现异常
比如找不到local host 或者不允许找
// 需要捕获异常,可能会出现异常
try {
// 1.获取本机的InetAddress对象 --- LAPTOP-EINLAL7G/192.168.56.1
InetAddress InetObject1 = InetAddress.getLocalHost();
System.out.println("InetObject1: "+ InetObject1);
// 2.根据指定主机名获取InetAddress对象 --- LAPTOP-EINLAL7G/192.168.56.1
InetAddress InetObject2 = InetAddress.getByName("LAPTOP-EINLAL7G");
System.out.println("InetObject2: "+ InetObject2);
// 3.根据域名返回InetAddress对象 --- www.taobao.com/60.163.129.165
InetAddress InetObject3 = InetAddress.getByName("www.taobao.com");
System.out.println("InetObject3: "+ InetObject3);
// 4.通过InetAddress对象,获取对应的IP地址 --- 180.101.49.11
InetAddress InetObject = InetAddress.getByName("www.baidu.com");
System.out.println(InetObject.getHostAddress());
// 5.通过InetAddress对象 获取对应的主机名或者域名 --- www.jd.com/60.165.115.3
InetObject = InetAddress.getByName("www.baidu.com");
System.out.println(InetObject.getByName("www.jd.com"));
}catch(Exception e){}
Socket
编程方式:1.TCP编程 可靠 2.UDP编程 不可靠
TCP网络通信编程
客户端 <==> 服务端
发送一次数据案例(字节流)
题目要求如下
- 编写一个服务端和一个客户端
- 服务端在9999端口监听
- 客户端连接服务端并发送一串字符串(字节数组) 然后退出
- 服务端接收到客户端发送的信息 输出并退出
实现代码
客户端
public class ClientTCP {
/**
* 客户端
* 思路:
* 1.连接服务端(ip,端口)
*/
public static void main(String[] args) throws IOException {
// 1.连接服务端(ip,端口)
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
// 2.连接上服务端,生成socket 并通过socket.getOutputStream()写入数据
OutputStream outputStream = socket.getOutputStream();
// 输入想要传输的字符串进行传输
System.out.println("请你输入想要传输的字符串");
String str = new Scanner(System.in).next();
byte[] out = str.getBytes();
outputStream.write(out) ;
System.out.println("传输成功!!");
// 3.最后要释放流 and socket
outputStream.close();
socket.close();
System.out.println("客户端结束传输");
}
}
服务端
public class TCPServer {
/**
* 服务端
* 思路:
* 1.设置监听端口为9999
* 2.监听客户端,是否有建立连接
* 如果建立连接则通过socket.getInputStream()接受数据
* 如果没有建立连接则一直等待直到建立连接
* 3.最后要释放流 and socket serverSocket
*/
public static void main(String[] args) throws IOException {
// 1.设置监听端口为9999
ServerSocket serverSocket = new ServerSocket(9999);
// 2.监听客户端,是否有建立连接
// 如果没有建立连接则一直等待直到建立连接
System.out.println("开始接收数据....");
Socket socket = serverSocket.accept();
// 如果建立连接则通过socket.getInputStream()接受数据
// 如果没有传入数据则等待数据的传入
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLength = 0;
while((readLength = inputStream.read(buf)) != -1){
System.out.println("接收到数据:"+new String(buf,0,readLength));
}
// 3.最后要释放流 and socket serverSocket
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务端结束传输");
}
}
实现结果
数据往返传输案例 (字节流)
题目要求如下
- 编写一个服务端和一个客户端
- 服务端在9999端口监听
- 客户端连接服务端并发送一串字符串(字节数组)并且接收到服务端传来的数据并且显示 然后退出
- 服务端接受到客户端发送的信息并传送回一串字符串(字节数组) 然后退出
注意点:socket传输完需要添加结束标记 如:socket.shutdownInput(); // 关闭输入流 socket.shutdownOutput(); // 关闭输出流
客户端
public class ClientTCP {
/**
* 客户端
* 思路:
* 1.连接服务端(ip,端口)
* 2.连接上服务端,生成socket 并通过socket.getOutputStream()写入数据
* 3.接收服务器传来的数据并显示
* 4.最后要释放流 and socket
*/
public static void main(String[] args) throws IOException {
// 1.连接服务端(ip,端口)
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
// 2.连接上服务端,生成socket 并通过socket.getOutputStream()写入数据
OutputStream outputStream = socket.getOutputStream();
// 输入想要传输的字符串进行传输
System.out.println("请你输入想要传输的字符串");
String str = new Scanner(System.in).next();
byte[] out = str.getBytes();
outputStream.write(out) ;
System.out.println("传输成功!!");
socket.shutdownOutput(); // 关闭输出流
// 3.接收服务器传来的数据并显示
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLength = 0;
while((readLength = inputStream.read(buf)) != -1){
System.out.println("接收到数据:"+new String(buf,0,readLength));
}
// 4.最后要释放流 and socket
inputStream.close();
outputStream.close();
socket.close();
System.out.println("客户端结束传输");
}
}
服务端
public class TCPServer {
/**
* 服务端
* 思路:
* 1.设置监听端口为9999
* 2.监听客户端,是否有建立连接
* 如果建立连接则通过socket.getInputStream()接受数据
* 如果没有建立连接则一直等待直到建立连接
* 3.接受到数据并且传送回一串字符串
* 4.最后要释放 流 and socket and serverSocket
*/
public static void main(String[] args) throws IOException {
// 1.设置监听端口为9999
ServerSocket serverSocket = new ServerSocket(9999);
// 2.监听客户端,是否有建立连接
// 如果没有建立连接则一直等待直到建立连接
System.out.println("开始接收数据....");
Socket socket = serverSocket.accept();
// 如果建立连接则通过socket.getInputStream()接受数据
// 如果没有传入数据则等待数据的传入
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLength = 0;
while((readLength = inputStream.read(buf)) != -1){
System.out.println("接收到数据:"+new String(buf,0,readLength));
}
socket.shutdownInput(); // 关闭输入流
// 3.接受到数据并且传送回一串字符串
System.out.println("想要输出撒?");
OutputStream outputStream = socket.getOutputStream();
String str = new Scanner(System.in).next();
outputStream.write(str.getBytes());
System.out.println("输出成功!!!");
// 4.最后要释放流 and socket serverSocket
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务端结束传输");
}
}
实现结果
数据往返传输案例 (字符流)
题目要求如下
- 编写一个服务端和一个客户端
- 服务端在9999端口监听
- 客户端连接服务端并发送一串字符串并且接收到服务端传来的数据并且显示 然后退出
- 服务端接受到客户端发送的信息并传送回一串字符串 然后退出
演示核心代码
// 客户端
String str = new Scanner(System.in).next();
// 转换流重点:An OutputStreamWriter is a bridge from character streams to byte streams
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write(str) ;
bufferedWriter.newLine(); // 换行符 表示写入的内容结束 注意对方必须是使用readline()
bufferedWriter.flush(); // 如果使用的是字符流,需要手动去刷新,否则数据将无法写入数据通道
System.out.println("传输成功!!");
// 服务端
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
System.out.println(bufferedReader.readLine());
// 注意关闭外层流
bufferedWriter.close();
bufferedReader.close();
发送一张图片
题目要求如下
- 编写一个服务端和一个客户端
- 服务端在8899端口监听
- 客户端连接到服务端,发送一张图片 地址:C:\Users\Ushop\Desktop\mess\Java\Al_tair.png
- 服务端接受到客户端发送的图片,保存到src下,然后再发送“收到图片”再退出
- 客户端接受到“收到图片”再退出
我遇到了一点小问题,暂时还不清楚原因:只能传输.jpg图片,不能传输.png图片
代码如下
客户端
public class TCPFileuploadClient {
/**
* 客户端
* 1. 编写一个服务端和一个客户端
* 2. 服务端在8899端口监听
* 3. 客户端连接到服务端,发送一张图片 地址:C:\Users\Ushop\Desktop\mess\Java\Al_tair.png
* 4. 服务端接受到客户端发送的图片,保存到src下,然后再发送“收到图片”再退出
* 5. 客户端接受到“收到图片”再退出
*/
public static void main(String[] args) throws Exception {
// 1. 连接到服务端8899端口
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
// 2.创建读取磁盘文件放入输入流
String filePath = "C:\\Users\\Ushop\\Desktop\\JavaLoad\\JavaSe\\Image\\pandas.jpg";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
// 3.通过socket获取到输入流,将byte数据发送到服务端
byte[] bytes = StreamUtils.streamToByteArray(bis);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes);
socket.shutdownOutput(); // 结束输出流
// 4.接受服务端发来的“收到图片”
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(reader.readLine());
// 5.关闭相关资源
reader.close();
bis.close();
bos.close();
socket.close();
}
}
服务端
public class TCPFileuploadServer {
/**
* 服务端
* 1. 编写一个服务端和一个客户端
* 2. 服务端在8899端口监听
* 3. 客户端连接到服务端,发送一张图片 地址:C:\Users\Ushop\Desktop\mess\Java\Al_tair.png
* 4. 服务端接受到客户端发送的图片,保存到src下,然后再发送“收到图片”再退出
* 5. 客户端接受到“收到图片” 再退出
*/
public static void main(String[] args) throws Exception {
// 1.服务端在本地监听8899端口
ServerSocket serverSocket = new ServerSocket(9999);
// 2.服务端等待连接
System.out.println("等待客户端发送数据.....");
Socket socket = serverSocket.accept();
// 3.读取客户端发送的数据 通过Socket得待输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
// 4.将得到的byte数组,写入到指定的路径,就得到一个文件了
String descFilePath = "E:\\Java_training\\Java_code\\JavaIdea03\\java\\Javase_HSping\\src\\com\\Al_tair\\socket\\upload\\pandas.jpg";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFilePath));
bos.write(bytes);
// 5.向客户端发送“收到图片”
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("收到图片");
writer.newLine();
writer.flush();
// 6.最后关闭其他资源
bis.close();
bos.close();
writer.close();
socket.close();
serverSocket.close();
}
}
工具类 :将.转换成byte[]字节数组
public class StreamUtils {
/**
* 功能:将输入流转换成byte[]字节数组
*/
public static byte[] streamToByteArray(InputStream inputstream) throws Exception{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 创建输出流对象
byte[] b = new byte[1024]; // 字节数组
int len;
while((len = inputstream.read(b))!= -1){
byteArrayOutputStream.write(b,0,len);
}
byte[] array = byteArrayOutputStream.toByteArray(); // 将输出流转换成字节数组
byteArrayOutputStream.close();
return array;
}
}
netstat 指令(补充)
-
netstat -an 可以查看当前主机网络情况,包括端口监听和网络连接情况
-
netstat -an | more 可以分页显示 ctrl+c退出该指令
-
dos控制行以管理员身份打开 netstat -anb 可以查看哪个应用软件监听该端口
-
当客户端连接到服务端后实际上客户 TCP/IP随机分配的端口(验证:netstat观测)
UDP网络通信编程[了解]
基本介绍
- 类DatagramSocket 和 DatagramPacke[数据包/数据报] 实现了基于UDP 协议网络程序
- UDP数据报套接字DatagramSocket 发送和接受数据,系统不保证UDP数据报一定能安全送达目的地也不能确定什么时候可以送到
- DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP 地址和端口号以及接收端的IP地址和端口号
- UDP协议中每个数据报都给出了完整的地址信息. 因此无需建立连接
UDP网络通信编程案例
任务要求:
- 编写一个接受端A和一个发送端B
- 接受端A 在9999端口等待接收数据报
- 发送端B向接收端A发送数据“hello~”
- 接收端A接收到发送端B发送的数据回复“hello , Im fine”,再退出
- 发送端B接收到回复的数据再退出
但是注意程序启动顺序还是有区别的,接收方要先启动程序!!!
public class udpNodeB {
/**
* Node B
* 1. 编写一个接受端A和一个发送端B
* 2. 接受端A 在9999端口等待接收数据报
* 3. 发送端A向接收端B发送数据“hello~”
* 4. 接收端B接收到发送端A发送的数据回复“hello , Im fine”,再退出
* 5. 发送端A接收到回复的数据再退出
*/
public static void main(String[] args) throws IOException {
// 1.创建一个DatagramSocket对象,准备在9998端口接收数据
DatagramSocket socket = new DatagramSocket(9998);
// 2.将需要发送的数据封装到 DatagramPacket对象 (UDP协议 数据包MAX 64K)
System.out.println("开始聊天界面...");
String str = new Scanner(System.in).next();
byte[] data = str.getBytes();
// 3.封装的 DatagramPacket对象组成: data 数据 | data.length 数据长度 | 主机(IP) | 端口(port)
// send 方法发送数据
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.56.1"), 9999);
socket.send(packet);
System.out.println("数据发送成功!!");
// 复制接受端A的j数据的方式
byte[] buf = new byte[64*1024];
packet = new DatagramPacket(buf,buf.length);
socket.receive(packet);
String s = new String(packet.getData(), 0, packet.getLength());
System.out.println(s);
// 5.释放资源
socket.close();
}
}
public class udpNodeA {
/**
* Node A
* 1. 编写一个接受端A和一个发送端B
* 2. 接受端A 在9999端口等待接收数据报
* 3. 发送端B向接收端A发送数据“hello~”
* 4. 接收端A接收到发送端B发送的数据回复“hello , Im fine”,再退出
* 5. 发送端B接收到回复的数据再退出
*/
public static void main(String[] args) throws IOException {
// 1.创建一个DatagramSocket对象,准备在9999端口接收数据
DatagramSocket socket = new DatagramSocket(9999);
// 2.创建一个 DatagramPacket对象,准备接收数据 (UDP协议 数据包MAX 64K)
byte[] buf = new byte[64*1024];
DatagramPacket packet = new DatagramPacket(buf,buf.length);
// 3.调用接收方法,将通过网络传输的DatagramPacket对象填充到packet对象
// receive方法会在9999端口一直等待直到接收到数据
System.out.println("开始接受数据...");
socket.receive(packet);
// 4.可以把packet进行拆包,取出数据并显示
String s = new String(packet.getData(), 0, packet.getLength());
System.out.println(s);
System.out.println("数据接收成功...");
// 复制发送端B的发送数据的方式
String str = new Scanner(System.in).next();
byte[] data = str.getBytes();
packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.56.1"), 9998);
socket.send(packet);
// 5.释放资源
socket.close();
}
}
相关面试题
1.讲一下TCP/IP协议
-
TCP/IP协议定义
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。
-
TCP/IP协议组成
TCP/IP结构模型分为**应用层、传输层、网络层、链路层(网络接口层)**四层,以下是各层的详细介绍:
(1)应用层
应用层是TCP/IP协议的第一层,是直接为应用进程提供服务的。
a. 对不同种类的应用程序它们会根据自己的需要来使用应用层的不同协议,邮件传输应用使用了SMTP协议、万维网应用使用了HTTP协议、远程登录服务应用使用了有TELNET协议。
b. 应用层还能加密、解密、格式化数据。
c. 应用层可以建立或解除与其他节点的联系,这样可以充分节省网络资源。
(2)传输层
作为TCP/IP协议的第二层,运输层在整个TCP/IP协议中起到了中流砥柱的作用。且在运输层中,TCP和UDP也同样起到了中流砥柱的作用。
(3)网络层
网络层在TCP/IP协议中的位于第三层。在TCP/IP协议中网络层可以进行网络连接的建立和终止以及IP地址的寻找等功能。
(4)链路层(网络接口层)
在TCP/IP协议中,网络接口层位于第四层。由于网络接口层兼并了物理层和数据链路层。所以,网络接口层既是传输数据的物理媒介,也可以为网络层提供一条准确无误的线路。
-
TCP/IP协议特点
TCP/IP协议能够迅速发展起来并成为事实上的标准,是它恰好适应了世界范围内数据通信的需要。它有以下特点:
(1)协议标准是完全开放的,可以供用户免费使用,并且独立于特定的计算机硬件与操作系统;
(2)独立于网络硬件系统,可以运行在广域网,更适合于互联网;
(3)网络地址统一分配,网络中每一设备和终端都具有一个唯一地址;
(4)高层协议标准化,可以提供多种多样可靠网络服务。
2.介绍一下tcp的三次握手
- 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
3.介绍一下TCP和UDP的区别
TCP和UDP有如下区别:
- 连接:TCP面向连接的传输层协议,即传输数据之前必须先建立好连接;UDP无连接。
- 服务对象:TCP点对点的两点间服务,即一条TCP连接只能有两个端点;UDP支持一对一,一对多,多对一,多对多的交互通信。
- 可靠性:TCP可靠交付:无差错,不丢失,不重复,按序到达;UDP尽最大努力交付,不保证可靠交付。
- 拥塞控制/流量控制:有拥塞控制和流量控制保证数据传输的安全性;UDP没有拥塞控制,网络拥塞不会影响源主机的发送效率。
- 报文长度:TCP动态报文长度,即TCP报文长度是根据接收方的窗口大小和当前网络拥塞情况决定的;UDP面向报文,不合并,不拆分,保留上面传下来报文的边界。
- 首部开销:TCP首部开销大,首部20个字节;UDP首部开销小,8字节(源端口,目的端口,数据长度,校验和)。
- 适用场景(由特性决定):数据完整性需让位于通信实时性,则应该选用TCP 协议(如文件传输、重要状态的更新等);反之,则使用 UDP 协议(如视频传输、实时通信等)。