• 0913(051天 网络编程01 Socket+URL)


    09130915(051053天 网络编程

    每日一狗(田园犬西瓜瓜

    在这里插入图片描述

    网络编程

    课前准备

    tcp/ip分层协议栈(七层架构):

    在这里插入图片描述

    tcp的三握四挥:

    三次握手

    • 客户端:我要和你创建连接了,你准备一下吧
    • 服务器:我准备好了,你链接吧
    • 客户端:好的,连接创建确定

    四次挥手

    • 客户端:我要断开连接了,你准备一下
    • 服务器:别忙我这还有活没玩,等会
    • 服务器:活干完了,断开连接吧
    • 客户端:好的,断开连接确定

    背:.tcp断开链接为什么时四挥,能不能三挥?

    因为其中还有一段时间需要将工作干完了才能断开

    HTTP1.0和1.1的区别:

    tcp和udp的区别:

    TCP面向连接的协议UDP 用户数据协报议
    是否需要建立连接是,三握四挥传输数据之前源端和终端不建立连接
    是否安全
    有没有重传机制为保证安全需要重传和对传输数据进行正确性验证
    思想就是安全就是快
    应用场景像是直播一样对数据的时效性要求很高的数据

    1. 啥也不是

    URL

    URL对象代表统一资源定位器,是指向互联网资源的指针

    http协议的标准端口为80 https=http+SSL 加密数据传输的http,标准端口为443

    • URL统一资源指针,可以指定一个具体的资源,例如一个html网页
    • URI统一资源标识符,可以认为为了方便记忆,给URL起的别名
    // 创建一个URL对象,用于指代网络中的一个资源,如果网址不合法,则抛出MalformedURLException
    
    URL url = new URL("url");
    
    // 重要方法
    url.openConnection()URLConnection//   可以获取输入、输出流
    url.openStream()InputStream//   直接获取输入流 
    
    // 不重要的方法:通过URL对象的一些方法可以访问该URL对应的资源:
    
    String getFile()://获取该URL的资源名
    String getHost()://获取主机名
    String getPath()://获取路径部分
    int getPort()://获取端口号 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    URL和URLConnection比较

    URL:统一资源指针

    URLConnection:一种连接

    URL url = new URL("https://zhuanlan.zhihu.com/p/24860273");
    InputStream is = url.openStream();
    
    URLConnection conn = url.openConnection();
    InputStream is1 = conn.getInputStream();
    OutputStream os1 = conn.getOutputStream();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    URL只能获取输入流,但是URLConnection可以获取输入输出流

    TCP编程

    TCP

    • 提供可靠的数据通讯
    • 面向连接的端对端的保证可靠传输的协议
    • 可以得到一个顺序的无差错的数据流
    • 建立连接后可以互相通信

    UDP

    • 不保证数据的可靠性
    • 协议简单、传输速度块(如音视频传输,这些不需要很高的可靠性,允许偶尔丢包)

    Socket

    Socket套接字,就是两台主机之间逻辑连接的端点。

    TPC协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket本质上就是一组接口,是对TCP/IP协议的封装和应用(程序员层面上)

    端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。

    客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

    开放端口包括三种:

    • 0.0.0.0:端口号
    • 127.0.0.1:端口号(只供本机访问的端口)
    • 主机ip:端口号

    cmd命令查看端口使用情况netstat -an

    • LISTENING是指开放着的,等待连接的
    • ESTABLISHED是正在连接
    • CLOSE_WAIT、TIME_WAIT、SYN_SENT是三次握手四次挥手过程中的某些状态
    ServerSocket类

    Java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是监听客户端的请求,然后将客户端的请求连接存入队列中,默认请求队列大小是50。

    端口扫描

    //利用的是ServerSocket在创建时,如果端口已经被占用,则报异常
    for(int i=0;i<=65535;i++) { //端口号0表示使用自由端口,实际上是不能建立连接的
     try {
         ServerSocket ss=new ServerSocket(i);
         ss.close();  //finally
     } catch (Exception e) {
         System.out.println("端口"+i+"已经被占用");
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    客户端Socket构造器
    Socket( // 
        String host, 
        int port, 
        InetAddress localAddr, 
        int localPort
    );
    
    Socket( // InetAddress对象中可以封装一个IP地址和一个主机名
        InetAddress address, 
        int port, 
        InetAddress localAddr, 
        int localPort
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    Socket练习题

    **1、**客户端与服务器端相互通讯

    // 客户端发送:
    来个套餐
    
    // 服务器回复:
    欢迎光临
    这个套餐没了,明天吧
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // 服务器端
    // 构建服务器对象
    ServerSocket ss = new ServerSocket(9009);
    // 请求阻塞
    Socket socket = ss.accept();
    // 获取连接的输入输出流
    InputStream is = socket.getInputStream();
    OutputStream os = socket.getOutputStream();
    // 使用输出流构建ps方便输出
    PrintStream ps = new PrintStream(os);
    // 发送信息
    ps.println("欢迎光临");
    // 使用输入流构建bf方便读取
    BufferedReader bf = new BufferedReader(new InputStreamReader(is));
    // 接受并输出信息
    String str = bf.readLine();
    System.out.println(str);
    // 发送信息
    ps.println("这个套餐没了,明天吧");
    // 关闭资源
    bf.close();
    ps.close();
    socket.close();
    ss.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    // 客户端
    // 与指定服务器的指定端口建立连接
    Socket socket = new Socket("127.0.0.1", 9009);
    // 获取连接的输入输出流
    InputStream is = socket.getInputStream();
    OutputStream os = socket.getOutputStream();
    
    // 读取并显示
    BufferedReader bf = new BufferedReader(new InputStreamReader(is));
    String str = bf.readLine();
    System.out.println(str);
    // 发送信息
    PrintStream ps = new PrintStream(os);
    ps.println("来个套餐");
    // 获取返回信息并显示
    str = bf.readLine();
    System.out.println(str);
    // 关闭资源
    bf.close();
    ps.close();
    socket.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    2、

    3、

    简单的Client/Setver程序

    在这里插入图片描述

    需求:客户端传送字符串给服务区端

    **服务器端:**一般会常开一个端口来为客户端提供服务

    1. 创建ServerSocket对象,绑定监听端口
    2. 通过accept方法监听客户端请求
    3. 连接建立后,通过输入流读取客户端发送的请求信息
    4. 通过输出流向客户端发送相应信息
    5. 关闭相应的资源对象
    ServerSocket server = new ServerSocket(9999);
    
    Socket socket = server.accept();
    
    InputStream is = socket.getInputStream();
    OutputStream os = socket.getOutputStream();
    PrintStream ps = new PrintStream(os);
    ps.println("hello");
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    
    String ss = br.readLine();
    System.out.println("Client:" + ss);
    // 传输数据为指定数据时返还当前时间数据
    if ("hello".equals(ss)) {
        Date now = new Date();
        DateFormat df = new SimpleDateFormat("yyyy-MM-DD-E hh:mm:ss");
        ss = df.format(now);
        ps.println(ss);
    
    }
    // 关闭资源
    br.close();
    ps.close();
    socket.close();
    server.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    客户端:

    1. 创建Socket对象,指明需求连接的服务器地址和端口号
    2. 连接建立后,通过输出流向服务器发送请求信息
    3. 通过输入流获取服务器响应的信息
    4. 关闭相应资源对象
    Socket socket = new Socket("127.0.0.1", 9999);
    InputStream is = socket.getInputStream();
    OutputStream os = socket.getOutputStream();
    
    // 传输数据
    PrintStream ps = new PrintStream(os);
    ps.println("hello");
    // 使用网络传输的输入流创建缓冲流
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    String ss = br.readLine(); // 阻塞程序
    System.out.println("Server:" + ss);
    
    System.out.println("Setver:" + br.readLine());
    
    // 关闭资源
    br.close();
    ps.close();
    socket.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    服务器端的编程思想

    主线程一直处于阻塞等待状态,如果一旦链接建立成功则启动一个新线程对外提供服务,而主线程继续等待新的链接请求。

    public class MyServer {
    	public static void main(String[] args) throws Exception {
    		ServerSocket server = new ServerSocket(9999);
    		while (true) {
    			Socket socket = server.accept();
    			new MyWorker(socket).start();
    		}
    	}
    }
    
    class MyWorker extends Thread {
    	private Socket socket;
    
    	public MyWorker(Socket socket) {
    		this.socket = socket;
    	}
    
    	@Override
    	public void run() {
    		BufferedReader br = null;
    		PrintStream ps = null;
    		try {
    			InputStream is = socket.getInputStream();
    			OutputStream os = socket.getOutputStream();
    			br = new BufferedReader(new InputStreamReader(is));
    			ps = new PrintStream(os);
    			String ss = br.readLine();
    			System.out.println("Client:" + ss);
    			if ("hello".equals(ss)) {
    				Date now = new Date();
    				DateFormat df = new SimpleDateFormat("yyyy-MM-ddE hh:mm:ss");
    				ss = df.format(now);
    				ps.println(ss);
    			}
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		} finally {
    			if (br != null)
    				try {
    					br.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			if (ps != null)
    				ps.close();
    			if (socket != null)
    				try {
    					socket.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    c/s和b/s区别

    • C/S (Client/Server,客户机/服务器)结构,胖客户端应用,是软件系统体系结构的一种。
      • C/S模式简单地讲就是基于企业内部网络的应用系统,大部分的应用逻辑都集中在客户端中,而一般服务器端只提供数据的存储。
      • 不依赖企业外网环境,即无论企业是否能够上网,都不影响应用。
    • B/S结构(Browser/Server结构)结构即浏览器和服务器结构,瘦客户端应用,主要逻辑集中在服务器端,客户端一般只包含的简单的显示逻辑。
      • 它是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。
      • 主要事务逻辑在服务器端Server实现,形成所谓三层3-tier结构。

    C/S聊天室的实现

    服务器端应用包含多个线程,每个Socket对应一个线程,该线程负责读取Socket对应输入流的数据,并将读到的数据向每个Socket发送一次

    每个客户端应该包含两个线程,一个负责读取用户键盘输入并将用户输入数据写入Socket对应的流中,一个负责读取Socket对应输入流的数据并打印显示


    3. 粘包

    数据包界限不清,先发送一个int值标识接下来的数据的长度,然后你读的时候就读这些,然后去读取下一个

    粘包问题主要是由于数据包界限不清,最好的解决办法就是在发送数据包前事先发送一个int型数据,该数据代表将要发送的数据包的大小,这样接收端可以每次触发OP_READ的时候先接受一个int大小的数据段到缓存池中,然后,紧接着读出后续完整的大小的包,这样就会处理掉粘包问题。因channel.read()方法不能给读取数据的大小的参数,所以无手动指定读取数据段的大小。但每次调用channel.read()返回的是实际读取的大小。

    怎么处理

    粘的时候搞一个分隔符、固定消息长度、TLV格式消息

    分隔符

    在这里插入图片描述

    固定消息长度

    在这里插入图片描述

    4. UDP网络程序

    1、使用DatagramSocket()创建一个数据包套接字。
    2、使用DatagramPacket(byte[]buf, int offset, int length, InetAddress address, int port)创建要发送的数据包。
    3、使用DatagramSocket类的send()方法数据包

    UDP网络通信的收包过程

    1、使用DatagramSocket(int)创建一个数据包套接字,绑定到指定的端口。
    2、使用DatagramPacket(byte[]buf,int length)创建字节数组来接收数据包.
    3、使用DatagramSocket类的receive()方法接收UDP

    获取的数据实际上就存储在创建空包的数组种,转换显示时建议设置长度
    System.out.println(dp.getLength()); //数据的具体长度
    String str=new String(buffer,0,dp.getLength());

    一般来说UDP协议的最大数据包的长度64k

    编码实现

    发送方

    package com.test;
    
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    
    public class Send {
    	public static void main(String[] args) throws Exception {
    		DatagramSocket ds = new DatagramSocket();
    		String msg = "now?";
    		byte[] buffer = msg.getBytes();
    		DatagramPacket dp = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 8888);
    		ds.send(dp); // 发送信息
    
    		buffer = new byte[8192];
    		dp = new DatagramPacket(buffer, buffer.length);
    		ds.receive(dp); // 接受信息
    		msg = new String(dp.getData(), 0, dp.getLength());
    		System.out.println("Send:" + msg);
    		ds.close();
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    接收方

    package com.test;
    
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class Rec {
    	public static void main(String[] args) throws Exception {
    		DatagramSocket ds = new DatagramSocket(8888);
    		byte[] buffer = new byte[8192];
    		DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
    		ds.receive(dp); // 接收信息
    		String ss = new String(buffer, 0, dp.getLength());
    		if ("now?".equals(ss)) {
    			Date now = new Date();
    			DateFormat df = new SimpleDateFormat("yyyy-MM-ddE hh:mm:ss");
    			ss = df.format(now);
    			InetAddress ia = dp.getAddress();
    			int port = dp.getPort();
    			buffer = ss.getBytes();
    			dp = new DatagramPacket(buffer, buffer.length, ia, port);
    			ds.send(dp); // 发送信息
    		}
    		ds.close();
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    多播或者组播的实现
    真正聊天室的实现原理

    MulticastSocket

    MulticastSocket可以将数据报以广播的方式发送给加入指定组的所有客户端

    组播是指把信息同时传递给一组目的地址。它使用的策略是最高效的,因为消息在每条网络链路上只需传递一次,且只有在链路分叉的时候,消息才会被复制。与多播相比,常规的点到单点传递被称作单播。当以单播的形式把消息传递给多个接收方时,必须向每个接收者都发送一份数据副本。由此产生的多余副本将导致发送方效率低下,且缺乏可扩展性。不过,许多流行的协议——例如XMPP,用限制接收者数量的方法弥补了这一不足

    5. 代理服务器

    JDK1.5提供了Proxy和ProxySelector类来实现代理访问。

    Proxy代表一个代理服务器,可以在打开URLConnection连接时指定代理,也可以在创建Socket连接时指定代理。ProxySelector是一个代理选择器,提供了对代理服务器更加灵活的控制,可以对http\https\ftp\socket等进行分别设置,还可以设置不需要通过代理服务器的主机和地址

    代理服务器的功能

    • 突破自身IP限制
    • 提高访问速度

    大作业 网盘

    要求:实现一个网盘管理功能

    上传、下载、删除、列表显示所有目录和文件

  • 相关阅读:
    一文总结 C++ 常量表达式、constexpr 和 const
    重写与重载and抽象类与接口!!!!
    【毕业设计】Spark海量新闻文本聚类(文本分类)
    电脑重装系统后Win10如何添加系统组件
    bert模型的参数量和时间复杂度分析
    Spring Boot
    .NET周刊【8月第3期 2023-08-20】
    (2022牛客多校四)A-Task Computing (排序+动态规划)
    java之二分查找算法
    c# 反射专题—————— 介绍一下是什么是反射[ 一]
  • 原文地址:https://blog.csdn.net/weixin_48015656/article/details/126843779