网络编程
1.软件结构
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。
2. 网络通信协议
上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能。
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
运输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
3. 协议分类
通信的协议还是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net 包中提供了两种常见的网络协议的支持:
UDP:
用户数据报协议(User Datagram
Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。
图片
数据报(Datagram):网络传输的基本单位
UDP适用于一次只传送少量数据,大小64kb
TCP:传输控制协议
(Transmission Control
Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
TCP的三次握手
TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
无法加载
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
为什么需要第三次通信 ?
在第一次通信过程中,客户端向服务端发送信息之后,服务端收到信息后可以确认自己的收信能力和客户端的发信能力没有问题。
在第二次通信中,服务端向客户端发送信息之后,客户端可以确认自己的发信能力和服务端的收信能力没有问题,但是服务端不知道自己的发信能力到底如何,所以就需要第三次通信。
在第三次通信中,客户端向服务端发送信息之后,服务端就可以确认自己的发信能力没有问题。
小结:
3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
TCP的四次挥手
建立连接非常重要,它是数据正确传输的前提;断开连接同样重要,它让计算机释放不再使用的资源。如果连接不能正常断开,不仅会造成数据传输错误,还会导致套接字不能关闭,持续占用资源,如果并发量高,服务器压力堪忧
过程:
1、 客户端发送关闭连接请求,并停止发送数据,等待服务器确认
2、服务器发送确认信息,通知客户端收到了断开连接请求,并继续发送剩余数据
3、 服务器发送完所有数据后,通知客户端所有信息发送完毕,可以断开
4、 客户端再次向服务器发送新,确认断开,并断开连接
过程描述
客户端:“任务处理完毕,我希望断开连接。”
服务端:“哦,是吗?请稍等,我准备一下。”
等待片刻后……
服务端:“我准备好了,可以断开连接了。”
客户端:“好的,谢谢合作。”
为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的连接请求后,可以直接发送确认信息和开启同步传输。
关闭连接时,当Server端收到客户端关闭连接请求,很可能并不会立即关闭SOCKET,所以只能先回复一个确认信息,告诉Client端,“你发的关闭连接请求我收到了”。
只有等到Server端所有的数据都发送完了,才能发送确认可以断开信息,因此不能一起发送。故需要四步握手。
4、网络编程三要素:IP协议,端口,传输协议(常见的有TCP,UDP)
本机IP地址:127.0.0.1、localhost 。
端口号:
用两个字节表示的整数,它的取值范围是0-65535。其中,0-1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
IP讲解:
IP地址
IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。
IP地址分类
IPv4:是一个32位的二进制数,通常被分为4个字节,表示成a.b.c.d 的形式,例如192.168.31.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。
为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
二、UDP编程
客户端:
DatagramSocket:不用传端口
DatagramPacket:数组,长度,ip,端口
方法:发送
public class Client {
public static void main(String[] args) throws IOException {
//创建套接字
DatagramSocket ds = new DatagramSocket();
String str = "今天周一,再忍4天就到周五了";
byte[] b = str.getBytes();
//ip地址 找另一台电脑进行相互的测试 注意:双方的防火墙要关闭
InetAddress ia = InetAddress.getByName("127.0.0.1");
//数据报包
DatagramPacket dp = new DatagramPacket(b, b.length, ia, 6666);//0-65535之间 1024之前已有使用,不建议使用1024
//发送 套接字发送报文到服务器
ds.send(dp);
//关闭
ds.close();
}
}
//读取本地文件传输给服务器
public static void main(String[] args) throws IOException {
// 创建套接字
DatagramSocket ds = new DatagramSocket();
InputStream is = new FileInputStream("file.txt");
byte[] b = new byte[1024];
int len = is.read(b);
String str = new String(b, 0, len);
// String str = "今天周一,再忍4天就到周五了";
byte[] b2 = str.getBytes();
// ip地址 找另一台电脑进行相互的测试 注意:双方的防火墙要关闭
InetAddress ia = InetAddress.getByName("127.0.0.1");
// System.out.println(ia);
// 数据报包
DatagramPacket dp = new DatagramPacket(b2, b2.length, ia, 6666);// 0-65535之间 1024之前已有使用,不建议使用1024
// 发送 套接字发送报文到服务器
ds.send(dp);
// 关闭
ds.close();
is.close();
}
服务端:
DatagramSocket:要传端口
DatagramPacket:
public class Server {
public static void main(String[] args) throws IOException {
//创建server套接字
DatagramSocket ds = new DatagramSocket(6666);
byte[] b = new byte[1024];
//创建报文对象
DatagramPacket dp = new DatagramPacket(b, b.length);
//调用receive方法
ds.receive(dp);
//转化为字符显示
String str = new String(b,0,dp.getLength());
System.out.println(str);
//关闭 通常是不关闭的
ds.close();
}
}
//接收客户端传输来的本地文件
public static void main(String[] args) throws IOException {
//创建server套接字
DatagramSocket ds = new DatagramSocket(6666);
byte[] b = new byte[1024];
//创建报文对象
DatagramPacket dp = new DatagramPacket(b, b.length);
//调用receive方法
ds.receive(dp);
//转化为字符显示
String str = new String(b,0,dp.getLength());
System.out.println(str);
//关闭 通常是不关闭的
ds.close();
}
三、TCP编程
//客户端
public class Client {
public static void main(String[] args) throws IOException {
//1创建客户端套接字
// 127.0.0.1:本地ip 也可以使用localhost
Socket s = new Socket("127.0.0.1", 8888);
//2调用getOutputStream获得输出流对象
OutputStream os = s.getOutputStream();
//3写数据到服务端
// String str = "中国奥运会获得38金牌";
// byte[] b = str.getBytes();
// os.write(b);
InputStream is = new FileInputStream("file.txt");
byte[] b2 = new byte[10240000];
int len = 0;
while((len = is.read(b2))>0) {
os.write(b2, 0, len);
}
//4关闭资源
os.close();
s.close();
}
}
//服务端
public class Server {
public static void main(String[] args) throws IOException {
//serverSocket :服务端套接字
ServerSocket ss = new ServerSocket(8888);
//2调用accept 返回客户端套接字
Socket s = ss.accept();
//3获得输入流对象
InputStream is = s.getInputStream();
//4定义一个数组
byte[] b = new byte[10240000];
int len =0;
//5while循环
while((len = is.read(b)) >0) {
//6组成String输出
String str = new String(b,0,len);
System.out.println(str);
}
//7关闭资源
is.close();
s.close();
ss.close();
}
}
download
public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1",10086);
//获得服务端的输出流
try(InputStream is = s.getInputStream();
OutputStream os = new FileOutputStream(UUID.randomUUID().toString().substring(0, 8)+".wma")
){
//读写过程
int len = 0;
byte[] b = new byte[1024*3];
while((len = is.read(b)) >0) {
os.write(b,0, len);
}
//关闭输出流
s.shutdownOutput();
InputStream is2 = s.getInputStream();
int len2 = 0;
byte[] b2 = new byte[1024];
while((len2 = is2.read(b2))>0) {
System.out.println(new String(b2,0,len2));
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10086);
//获得客户端的socket
Socket s = ss.accept();
//获得客户端的输出流
try(
OutputStream os = s.getOutputStream();
InputStream is = new FileInputStream("a.wma");
){
int len = 0;
byte[] b = new byte[1024*3];
while((len = is.read(b))>0) {
os.write(b, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
upload
public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1",6666);
try(
InputStream is = new FileInputStream("a.wma");
OutputStream os = s.getOutputStream();
){
int len = 0;
byte[] b = new byte[1024];
while((len = is.read(b)) >0) {
os.write(b, 0, len);
}
//写个-1到服务端
s.shutdownOutput();
InputStream is2 = s.getInputStream();
int len2 = 0;
byte[] b2 = new byte[1024];
while((len2 = is2.read(b2)) >0) {
System.out.println(new String(b2,0,len2));
}
} catch (Exception e) {
e.printStackTrace();
}
s.close();
}
}
public class Server3 {
public static void main(String[] args) throws IOException {
//创建服务端套接字
ServerSocket ss = new ServerSocket(6666);
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(30);
while(true) {
Socket s = ss.accept();
//写一个类封装到线程类里面
//try这块代码块写到run方法里面
// new 这个线程类,启动线程
Upload u = new Upload(s);
//提交任务
pool.submit(u);
}
}
}
public class Upload implements Runnable{
private Socket s;
public Upload(Socket s) {
this.s = s;
}
@Override
public void run() {
try(
InputStream is = s.getInputStream();
//下面这行代码把文件名写死了
//OutputStream os = new FileOutputStream("b.wma");
OutputStream os = new FileOutputStream(UUID.randomUUID().toString().substring(0, 8)+".wma");
OutputStream os2 = s.getOutputStream();
){
int len = 0;
byte[] b = new byte[1024];
while((len = is.read(b)) >0) {
os.write(b, 0, len);
}
//告诉客户端
os2.write("上传成功!".getBytes());
os2.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
netserver
//HTMLServer
public class HtmlServer3 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10086);
Socket s = ss.accept();
try(OutputStream os = s.getOutputStream();
//请示信息在inputStream里面 比如请求路径等
InputStream is = s.getInputStream();
//缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(is))
){
//获取请求的内容
String line = br.readLine();
// GET /web/index.html HTTP/1.1
System.out.println("line:" + line);
//用空格分割字符串
String[] resource = line.split(" ");
//路径 要的是/web/index.html
String path = resource[1].substring(1);//substring(1):从第2个字符到路径的末尾,去掉 /
//创建路径对象
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
String head = "HTTP/1.1 200 OK\r\n Content-Type text/html; charset=utf-8\r\n"
+ "\r\n\r\n";
os.write(head.getBytes("utf-8"));
int len = 0;
byte[] b = new byte[1024];
while((len = fis.read(b)) >0) {
os.write(b, 0, len);
os.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//SimpleServer 简单服务器
public class SimpleServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10086);
//循环响应
while(true) {
System.out.println("与客户端建立连接,准备响应...");
//接收客户端
Socket s = ss.accept();
try(
OutputStream os = s.getOutputStream();//往 浏览器写内容的输出对象
){
String str = "hello,地球人,我来自火星...";
//转成字节数组
byte[] b = str.getBytes("utf-8");
//设置响应头 \r\n\r\n 最后结束一定要写 不写浏览器不认为是结束 的标志
String head = "HTTP/1.1 200 OK\r\n Content-Type text/html; charset=utf-8\r\n"
+ " Content-Length:" + b.length +"\r\n\r\n";
//写内容出去
os.write(head.getBytes("utf-8"));
os.write(str.getBytes());
System.out.println("响应到页面...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
四、URL
4、1 加密(encode),解密(decode)
public class EncoderAndDecoder {
public static void main(String[] args) throws IOException {
//模拟浏览器地址栏输入的关键词
String str = "中国";
//编码
String encode = URLEncoder.encode(str, "utf-8");
System.out.println(str + "编码后:" + encode);
//%E4%B8%AD%E5%9B%BD
//%E4%B8%AD%E5%9B%BD
//模拟后台接收到的编码后的字符串
String str2 = "%E4%B8%AD%E5%9B%BD";
//解码
String decode = URLDecoder.decode(str2, "utf-8");
System.out.println(str2+"解码后:" + decode);
//使用场景:在hmtl/jsp又或者其它页面 防止中文乱码,在提交数据之前先进行编码,后台接收到字符串后进行解码
}
}
4、2 网上的链接下载
public class URLConnectionDemo {
public static void main(String[] args) throws IOException {
// 创建URL对象
// httpClient jsoup
URL url = new URL("https://haokan.baidu.com/v?pd=wisenatural&vid=8473942227336018908");
// 获得urlConnection对象
URLConnection conn = url.openConnection();
try (InputStream is = conn.getInputStream(); OutputStream os = new FileOutputStream(new File("D:/a.mp4"))) {
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b)) > 0) {
os.write(b, 0, len);
}
System.out.println("复制Ok....");
} catch (Exception e) {
e.printStackTrace();
}
}
}
UDP和TCP的区别
UDP和TCP编程区别
UDP是面向无连接的,不可靠的传输
UDP传输的大小在64KB以内,否则会报超出最大限制异常
UDP传输效率是更高的
UDP是轻量级协议
TCP面向连接,是可靠的连接
传输效率比UDP要低
传输大小没有限制
TCP协议是重量级协议