基础系列【十七】--Socket基础

Socket–套间字

基础知识

1.网络分层:
    为了解决网络之间通信的复杂问题 人为将网络分层 在不同层解决不同的问题 有不同的协议
    网络协议:网络中主机之间通信的规范

    OSI七层:
        应用层: HTTP FTP SMTP TELNET ...
        表示层:(传输的数据量)
        会话层: 
        传输层: TCP UDP(传输的方式)
        网络层: IP(传输的前提)
        数据链路层:保证数据的传输
        物理层 :物理连接

    TCP/IP四层:
        应用层
        传输层 
        网际层
        网络层  
2.计算机网络中的基本的要素
    IP地址(门牌号):
        计算在网络中的唯一标识
        ipv4 - 32位
            127.0.0.1 -- 指向当前机器
            255.255.255.255 -- 广播地址
        ipv6 - 128位

    主机名
        因为ip地址难于记忆,可以为不同的主机起不同的名字 来唯一标识 这个唯一标识主机的名字 成为主机名 和 ip地址具有映射关系
        localhost 代表本机
    域名
        全网认可的主机名 域名不能随意用 需要注册 先到先得 收取一定费用
    域名解析
        DNS服务器:域名解析服务器 接受域名 解析为 ip 返回

        Hosts文件:windows自带的机制 可以在本地做主机名 和ip 的映射
                在windows中使用主机名时 会先查找hosts文件 得到主机名和ip的映射 得到就用 得不到再求访问公网上的dns服务器翻译
                C:\Windows\System32\drivers\etc\hosts

    端口号(窗户号):
        每个计算机 都有 若干个 可以供外界连接的窗口 每个窗口都有独一无二的编号 程序可以监听指定的端口号 外界通过ip连接到当前机器后 将数据发送 给指定端口号 从而 让正在监听该端口的程序收到数据。
        范围:0~65535  其中0~1024是被系统占用了的,我们不能使用。

    协议:网络中主机之间通信的规范
        IP TCP/UDP  HTTP(网络通信协议) POP3 SMTP(邮箱) TELNET FTP(文件上传下载) ...

InetAddress

代表网络地址的对象		 
java.net.InetAddress -此类表示互联网协议 (IP) 地址
没有提供构造方法

提供了静态方法来获取:
    static InetAddress getLocalHost() //获取本机ip地址对象
    static InetAddress getByName(String host) //根据给定ip或主机名 获取ip地址对象
    String getHostName() //获取当前对象代表的主机的主机名
    String getHostAddress() //获取当前对象代表的主机的ip地址

代码演示

package cn.tedu.net;

import java.net.InetAddress;

/**
 * java网络开发 - InetAddress - 代表网络地址
 */
public class Demo01 {
    public static void main(String[] args) throws Exception {
        //1.获取自己的ip对象
//      InetAddress addr1 = InetAddress.getLocalHost();
//      String hostName  = addr1.getHostName();
//      String hostAddress = addr1.getHostAddress();
//      System.out.println(hostName + "~" + hostAddress);

        //2.获取其他的ip对象
        InetAddress addr2 = InetAddress.getByName("www.baidu.com");
        String hostName  = addr2.getHostName();
        String hostAddress = addr2.getHostAddress();
        System.out.println(hostName + "~" + hostAddress);

    }
}

UDP协议

不需要建立连接
将数据及源和目的地封装为数据包
每个数据报的大小限制在64k内
因为无连接,是不可靠协议

优点:快
缺点:会丢数据

TCP协议

建立连接,形成传输数据的通道
在连接中进行大数据量传输,靠流来进行操作
三次握手,是可靠协议
必须先建立连接,效率会稍低

优点:不丢数据
缺点:效率相对较低

Socket编程

什么是Scoket编程:
	利用Java提供的TCP/UDP协议开发的编程接口,
一套API,让我们通过调用API实现TCP/UDP通信。

Socket实现UDP

 java.net.DatagramSocket - UDP协议的编程接口类,负责收发数据包
        构造方法:
            DatagramSocket()//通常用来发送
            DatagramSocket(int port)//通常用来接收

        其他方法:
            void send(DatagramPacket p) //发送数据包
            void receive(DatagramPacket p) //接收数据包
            void close() //关闭套接字

    java.net.DatagramPacket - UDP的数据包,内含 数据 数据来源 数据目的地
        构造方法:
            DatagramPacket(byte[] buf, int length)//一般用做接收
            DatagramPacket(byte[] buf, int length, InetAddress address, int port) //一般用作发送


        其他方法:
            InetAddress getAddress()  
            int getLength()  
            byte[] getData() 
            int getPort()  
代码演示
/**
 * java网络通信 - UDP - 数据包发送
 */
public class Demo02 {
public static void main(String[] args) throws Exception {
    //1.创建UDP套接字
    DatagramSocket socket = new DatagramSocket();
    //2.创建UDP数据包
    byte[] data = "hello UDP~".getBytes();
    DatagramPacket p = new DatagramPacket(data, data.length,InetAddress.getByName("127.0.0.1"),44444);
    //3.通过UDP套接字 发送 UDP数据包
    socket.send(p);
    //4.关闭套接字
    socket.close();
}
}

/**
 * java网络通信 - UDP - 数据包接收
 */
public class Demo03 {
public static void main(String[] args) throws Exception {
    //1.创建UDP套接字
    DatagramSocket socket = new DatagramSocket(44444);
    //2.接收数据
    byte [] data = new byte [1024]; 
    DatagramPacket p = new DatagramPacket(data, data.length);
    socket.receive(p);
    //3.获取其中的信息
    String ip = p.getAddress().getHostAddress();
    int port = p.getPort();
    int length = p.getLength();
    // byte[] getData();返回接收数据包中缓存的数据
    String str = new String(p.getData());
    System.out.println(ip+"~"+port+"~"+length+"~"+str);
    //4.关闭套接字
    socket.close();
}
}
案例:实现单人聊天
/*
 * 单机聊天
 */
public class DemoT2 {
// 负责发送数据
public static void main(String[] args) {
    Scanner scanner = null;
    DatagramSocket socket = null;
    DatagramPacket p = null;
    try {
        while (true) {
            scanner = new Scanner(System.in);
            System.out.println("请输入你要发送的数据:");
            String msg = scanner.nextLine();
            // 创建UDP网络开发接口类,负责数据的收发
            socket = new DatagramSocket();
            byte[] data = msg.getBytes();
            // 创建UDP的数据包,包含数据 数据的长度 数据的目的地和端口
            p = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 8888);
            // 发送数据包
            socket.send(p);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 关闭资源
        if (scanner != null) {
            scanner.close();
        }
        if (socket != null) {
            socket.close();
        }
    }

}
}

class Rev {
// 负责接收数据
public static void main(String[] args) {
    DatagramSocket socket = null;
    try {
        // 创建UDP网络开发接口类,负责数据的收发
        socket = new DatagramSocket(8888);
        while (true) {
            byte[] data = new byte[1024];
            // 创建UDP的数据包,包含数据 数据的长度 数据的目的地和端口
            DatagramPacket p = new DatagramPacket(data, data.length);
            // 接收数据包中的数据
            socket.receive(p);
            // 将数据包中内部缓冲数组中的数据输出
            String str = new String(p.getData());
            // 获取客户端的ip
            String ip = p.getAddress().getHostAddress();
            // 获取服务端监听的端口号
            int port = p.getPort();
            System.out.println(ip+"通过"+port+"发送:"+str);
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally{
        socket.close();
    }
}
}
//想要实现广播:需把ip地址改成255.255.255.255即可
public class DemoT2 {
public static void main(String[] args) {
    new Thread(new Receiver()).start();
    new Thread(new Sender()).start();
    /***接收者启动成功!
    ***发送者启动成功!
    ----------------------
    请输入接受者ip和端口:
    localhost:44444
    请输入消息:
    loving
    ----------------------
    ----------------------
    请输入接受者ip和端口:
    ######客户端接收到cn.bing.com[127.0.0.1]发送的消息:loving*/
}
}
class Receiver implements Runnable{

@Override
public void run() {
    try {
        DatagramSocket socket = new DatagramSocket(44444);
        System.out.println("***接收者启动成功!");
        while(true){
            byte [] data = new byte[1024];
            DatagramPacket p = new DatagramPacket(data, data.length);
            socket.receive(p);
            String ip = p.getAddress().getHostAddress();
            String hostname = p.getAddress().getHostName();
            String msg = new String(p.getData());
            System.out.println("######客户端接收到"+hostname+"["+ip+"]"+"发送的消息:"+msg);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

class Sender implements Runnable{

@Override
public void run() {
    DatagramSocket socket = null;
    Scanner scanner = null;
    try {
        socket = new  DatagramSocket();
        scanner = new Scanner(System.in);

        System.out.println("***发送者启动成功!");

        while(true){
            System.out.println("----------------------");

            System.out.println("请输入接受者ip和端口:");
            String line = scanner.nextLine();
            String ip = line.split(":")[0];
            int port = Integer.parseInt(line.split(":")[1]);

            System.out.println("请输入消息:");
            String msg = scanner.nextLine();

            DatagramPacket packet = new DatagramPacket(msg.getBytes(), msg.getBytes().length,InetAddress.getByName(ip),port);

            socket.send(packet);
            System.out.println("----------------------");
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (socket!=null) {
            socket.close();
        }
        if(scanner!=null){
            scanner.close();
        }
    }
}

}

Socket实现TCP

需要经历三次握手建立连接 
用流来传输大量数据 
有数据包重发机制 实现可靠的数据传输 
可以双向传输数据

TCP基于连接工作 连接具有两端 称为服务器端 和 客户端 
把主动发起请求的一端 称为客户端 被动等待请求 接收连接的一方称之为 服务器端

代表soket地址的类:
    SocketAddress - InetSocketAddress
    构造方法:
        InetSocketAddress(InetAddress addr, int port) 
        InetSocketAddress(String hostname, int port) 
    其他方法:
        InetAddress getAddress()  
        String getHostName()  
        int getPort()  

客户端:
    java.net.Socket

    构造方法:
        Socket() 
        Socket(InetAddress address, int port) 
        Socket(String host, int port) 

    其他方法:
        void connect(SocketAddress endpoint) //开始连接服务器 
        void connect(SocketAddress endpoint, int timeout) //开始连接服务器,额外指定超时时间
        InetAddress getInetAddress()  //获取连接的地址
        int getPort() //获取连接的端口
        InputStream getInputStream() //获取输入流  
        OutputStream getOutputStream() //获取输出流
        void shutdownInput() //结束对输入流的读取,不会关闭流,只是无法再从流中读取数据
        void shutdownOutput() //结束对输出流的输入,不会关闭流,只是无法再向流中写出数据
        boolean isInputShutdown()  //判断输入流是否已经被置于结束状态
        boolean isOutputShutdown()  //判断输出流是否已经被置于结束状态
        void close()  //关闭套接字 自动关闭底层输入输出流

服务端:
    java.net.ServerSocket

    构造方法:
        ServerSocket() 
        ServerSocket(int port) 

    其他方法:
        Socket accept()  //开始接受连接 一旦调用 就进入阻塞状态 直到有一个客户端连接过来 才会解除阻塞继续运行
        void close()  //关闭套接字 自动关闭底层输入输出流
        InetAddress getInetAddress()  //获取ip
        int getLocalPort()  //获取本地监听端口 

C-S端通信模型

在这里插入图片描述

C-S的全称为(Client-Server):客户端-服务器端
    客户端与服务端通信模型如下:
    服务端创建ServerSocket 
    通过调用ServerSocket的accept方法监听客户端的连接 
    客户端创建Socket并指定服务端的地址以及端口来建立与服务端的连接 
    当服务端accept发现客户端连接后,获取对应该客户端的Socket 
    双方通过Socket分别获取对应的输入与输出流进行数据通讯 
    通讯结束后关闭连接

粘包问题

因为TCP协议本身是传输层的协议 
只负责数据的收发 并不会关心数据本身的意义 
所以当 连续发送多段数据时 TCP底层的流 会按照自身需要 将数据封装到数据包中发送
此时 同一段数据可能被 拆分到多个包中 也可能多段数据被放在同一个包中 
所以接收端 无法根据包来识别数据的分段 
经过tcp传输后 数据的分段的信息相当于丢失了 无法区分出数据应该从哪里分段    
这个问题 称之为tcp协议在应用中产生了粘包问题

如何判断边界

在这里插入图片描述
解决方法:
使用特定分隔符 标识数据之间的间隔
缺点:如果数据中本身就包含特定分隔符 则需要 进行转义

            发送固定长度的数据
                缺点:数据长度受到制约 对于短数据需要发送大量空数据浪费资源 对于长数据 有可能无法发送

            使用协议:
                利用协议规定数据传输的格式 来携带辅助信息解决粘包问题
                 **协议 是通信双方互相约定的通信交往的相关机制(通信的方式、数据的格式) 。
            私有协议--自定义的协议,只能在一定范围内使用。
            公共协议:是由国际组织 定义的通用的协议 称之为 公有协议。在企业开发中 两种协议都有所应用。
   	    相对来说 公有协议 通用 可靠 
            有协议 私密性更好 定制能力更强

下一篇:
HTML-JS-CSS-JQ

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值