Java 套接字Socket

套接字

一、概述

  1. 本质上是一套基于网络传输数据的流
  2. 实际上是一套用于网络通信的API
  3. IP地址
    1. IPv4是指由4组数组成的IP地址 , 每组数的范围在0~255 。一共有2的32次方个地址(43亿多)
    2. IPv6是由六组十六进制的数组成, 每组数的取值范围0~ffff 。 一共有2的96次方的地址 。
  4. 端口 : 用于和外界进行信息交互的媒介 。 计算机上有65535个端口 , 0~1024已经被系统内部以及一些常用应用占用。 在访问网站时 , 默认80端口 ,可以不写
  5. DNS服务器 : 将域名解析为对应的IP地址
  6. 网络模型
    1. 七层模型: 物理层 、 数据链路层、网络层(IP) 、 传输层(UDP\TCP) 、 会话层 、 应用层 、 表示层 (HTTP 、 HTTPS , FTP 、POP3 、 SMTP 、 TENLNET…)
    2. 四层: 物理层 、 数据链路层 、 网络层 、 应用层 。

二、SocketAddress类

  1. 本身是一个抽象类 。
  2. 其子类 InetSocketAdress , 实现IP地址套接字(IP地址+端口号) 。
  3. 代码:

    import java.net.InetSocketAddress;
    //地址类
    public class Test_01 {
        public static void main(String[] args) {
            //每次计算机介入互联网时都会被自动分配一个IP地址  , 每次都会变
            //127.0.0.1 永远指向本机
            //localhost也表示本机 
            InetSocketAddress isa = new InetSocketAddress("www.baidu.com",5555);
            //获取地址
            System.out.println(isa.getAddress());
            //获取计算机名
            //如果不是本机IP , 则试图从网络获取 , 如果获取失败则返回IP地址   
            System.out.println(isa.getHostName());
            //获取端口号
            System.out.println(isa.getPort());
        }
    }
    

三、UDP传输协议

  1. 基于流的 , 不需要建立连接直接发送 , 不能确保接收方能接收到 , 传输速度比较快 , 会对数据进行封包 ,每个包不超过64k。
  2. 适用于与一些要求速度而不要求可靠性的场景 。
  3. 代码:

    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetSocketAddress;
    
    //UDP发送端
    
    /*
     * 1. 创建套接字对象
     * 2. 数据封包
     * 3. 发送数据
     * 4. 关流
     * */
    public class UDPSender_01 {
        public static void main(String[] args) throws IOException {
            //UDP发送端的套接字对象
            DatagramSocket ds = new DatagramSocket();
            //对数据进行封包
            //第一个参数:字节数组 , 数组中放的是实际数据
            //第二个参数: 字节数组的大小 (数据的字节个数)
            //第三个参数:发送地址 
            DatagramPacket dp  =new DatagramPacket(
                    "你好".getBytes(), 
                    "你好".getBytes().length,
                    new InetSocketAddress("localhost" , 9999));
            //发送数据
            ds.send(dp);
            //关流
            ds.close();
        }
    }
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    
    //UDP接收端
    /*
     * 1. 创建套接字对象
     * 2. 准备数据报
     * 3. 接收数据
     * 4. 读取数据
     * */
    public class UDPReciever_01 {
        public static void main(String[] args) throws IOException {
            //创建套接字对象  , 并指定端口 
            DatagramSocket ds = new DatagramSocket(9999);
            //准备一个数据报
            //第一个参数: 一个字节数组 , 表示作为存储接收数据的容器
            //第二个参数: 表示指定容器的大小
            DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
            //接收数据
            //在没有接收到数据时处于阻塞状态 
            ds.receive(dp);
            //关流
            ds.close();
            //将数据从数据包中解析出来
            byte[] bs = dp.getData();//获取数据存储数据的直接数组 
            int len = dp.getLength();//获取实际数据长度
            System.out.println(new String(bs ,0, len));
    
            //可以获取发送地址
            System.out.println(dp.getAddress());
            //获取发送用的端口号   端口号会一直变 ,  这是为什么?
            System.out.println(dp.getPort());
        }
    }
    
  4. 练习:单人聊天室

    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.net.SocketException;
    import java.util.Scanner;
    
    //单人聊天 发送端
    public class UDPIMSender_03 implements Runnable {
    
        @Override
        public void run() {
            // 创建套接字
            try {
                DatagramSocket ds = new DatagramSocket();
                // 创建数据报
                Scanner in = new Scanner(System.in);
                //获取数据
                while(true){
                    System.out.println("回车发送:");
                    String str = in.nextLine();
                    DatagramPacket dp = new DatagramPacket(str.getBytes(),
                            str.getBytes().length, new InetSocketAddress("localhost",
                                    10000));
                    // 发送数据
                    ds.send(dp);
                    if(str.equals("over")){
                        System.out.println("发送端结束");
                        break;
                    }
                }
                //关流
                ds.close();
    
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }
    }
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.SocketException;
    
    //UDP聊天室  接收端
    public class UDPIMReciever_03  implements Runnable{
    
        @Override
        public void run() {
            //创建套接字对象
            try {
                DatagramSocket ds = new DatagramSocket(10000);
                //创建数据包
                DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
    
                //接收数据
                while(true){
                    ds.receive(dp);
                    //读取数据
                    byte[] data = dp.getData();
                    String str = new String(data , 0 , data.length);
                    if("over".equals(str.trim())){
                        System.out.println("接收端结束");
                        break;
                    }
                    System.out.println("客户端发来:"+str);
                }
                //关流
                ds.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    //单人聊天
    public class Test_03 {
        public static void main(String[] args) {
            //开启聊天
            new Thread(new UDPIMReciever_03()).start();
            new Thread(new UDPIMSender_03()).start();
        }
    }
    

四、TCP传输协议

  1. 基于流的
  2. 发送数据前 , 需要经过三次握手 。是可靠地安全的连接 。 但传输效率相对较低 。
  3. 理论上不限制数据大小 。
  4. 适用于要求可靠性而不要求速度的场景 。
  5. 一般分为客户端和服务器端
  6. 注意: receive 、 connect 、 accept 、 read 、 write 都会产生阻塞
  7. 代码:

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class TCPServer_01 {
        public static void main(String[] args) throws IOException {
            //创建套接字 , 并绑定端口号
            ServerSocket ss = new ServerSocket(7000);
            //接受连接
            //阻塞
            Socket s = ss.accept();
            //读取数据
            InputStream is = s.getInputStream();
            byte[] bs = new byte[1024];
            int len = -1;
            while((len = is.read(bs))!=-1){
                System.out.println(new String(bs));
            }
            //向客户端响应信息
            OutputStream out = s.getOutputStream();
            out.write("接受成功!".getBytes());
            //关流
            ss.close();
            s.shutdownOutput();
        }
    }
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    
    public class TCPCilent_01 {
        public static void main(String[] args) throws IOException {
            //创建客户端套接字对象
            Socket s = new Socket();
            //客户端发起连接 , 绑定链接地址
            //连接时会发生阻塞
            s.connect(new InetSocketAddress("localhost", 7000));
            //获取一个输出流
            OutputStream out = s.getOutputStream();
            //写出数据
            out.write("你好".getBytes());
            //通知服务器端 数据已经写出完毕
            s.shutdownOutput();
            //接服务器端响应
            InputStream in = s.getInputStream();
            byte[] bs = new byte[1024];
            int len  = -1;
            while((len = in.read(bs)) != -1){
                System.out.println(new String(bs));
            }
            //关流
            s.close();
        }
    }
    
  8. 练习: 上传文件 — 文件名不能变

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class TCPServer_04 {
        public static void main(String[] args) throws IOException {
            //创建套接字对象 , 并指定端口
            ServerSocket ss = new ServerSocket(7777);
            //接受连接
            Socket s = ss.accept();
            //获取输入流
            InputStream is = s.getInputStream();
            //读取文件名字节个数
            int  count= is.read();
            //读取文件名
            byte[] name = new byte[count];
            is.read(name);
            //创建文件实例
            File file = new File("C:\\Users\\user\\Desktop\\暂存\\副本 -- "+new String(name));
            FileOutputStream fos = new FileOutputStream(file);
    
            //读取数据
            int len  = -1 ; 
            byte bs[] = new byte[1024];
            while((len = is.read(bs)) != -1){
                System.out.println(new String(bs  , 0, len));
                fos.write(bs, 0, len);
            }
            s.shutdownInput();
            fos.close();
            System.out.println("接收成功");
            //向客户端响应信息
            OutputStream out = s.getOutputStream();
            out.write("接收完成!".getBytes());
            //关流
            s.shutdownOutput();
        }
    }
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    //上传文件客户端
    
    public class TCPClient_04 {
        public static void main(String[] args) throws IOException {
            //创建文件实例
            File file = new File("C:\\Users\\user\\Desktop\\暂存\\c.txt");
            //获取文件名字
            String name = file.getName();
            //创建套接字
            Socket s= new Socket();
            //建立连接
            s.connect(new InetSocketAddress("localhost" , 7777));
            //获取输出流
            OutputStream out = s.getOutputStream();
            //输出文件名字节个数
            out.write(name.length());
            //输出文件名
            out.write(name.getBytes());
            //读取文件并写出
            FileInputStream fis = new FileInputStream(file);
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            String line = null;
            while((line  = br.readLine()) != null){
                out.write(line.getBytes());
            }
            //通知服务端写出完毕
            s.shutdownOutput();
            //向客户端响应信息
            InputStream in = s.getInputStream();
            byte[] inbs =  new byte[1024];
            in.read(inbs);
            if(new String(inbs).trim().equals("接收完成!")){
                System.out.println("发送成功");
            }else{
                System.out.println("发送失败");
            }
            //关流
            s.shutdownInput();
        }
    }
    

五、扩展

  1. BIO — BlockingIO 同步式阻塞式IO
  2. NIO — NewIO – NonBlockingIO JDK1.4出现 同步式非阻塞式IO - 框架:Mina Netty
  3. AIO — AsynchronousIO 异步式非阻塞式IO JDK1.8
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值