深入Socket-初探Socket

本文借鉴此博客

什么是Socket
  • 简单来说是IP地址与端口的结合协议
  • 一种地址与端口的结合描述协议
  • TCP/IP协议的相关API的总称;是网络API的集合实现
  • 涵盖了Stream Socket/Datagram Socket
协议简介

协议相当于相互通信的程序间达成的一种约定,规定了分组报文的结构,交换方法以及怎样对报文进行解析等。

  1. TCP/IP协议族中包含IP协议,TCP协议,UDP协议。其中TCP协议和UDP协议使用的地址叫做端口号,端口号用来区分统一主机上不同的应用程序,这两者协议也叫做端到端的传输协议,因为他们将数据从一个应用程序传输到另一个应用程序;而IP协议只是将数据从一个主机传输到另一个主机。

  2. TCP/IP协议中,有两部分信息用来确定一个指定的程序:互联网地址和端口号:其中互联网地址由IP协议使用,而附加的端口地址信息则由传输协议TCP/IP进行解析

  3. 现在TCP/IP协议族中的主要socket类型为流套接字(使用TCP协议)和数据报套接字(使用UDP协议)

  4. 一个TCP/IP套接字由一个互联网地址,一个端对端的协议(TCP或UDP)以及一个端口号确定

  5. 每个端口标识了主机上一个应用程序,实际上,一个端口确定了一个主机上的一个套接字。主机中的多个程序可以同时访问同一个套接字


基本套接字
  1. 编写TCP客户端程序时,实例化Socket类时,要注意底层的TCP协议只能处理IP协议,如果传入的参数是主机名字而不是IP地址,Socket具体实现的时候会将其解析成相应的地址,若因为某些原因失败,构造函数会抛出IO Exception异常
  2. TCP协议读写数据时,read()方法在没有可读数据时会阻塞等待,直到有新的数据可读。另外TCP协议并不能确定在read()和write()方法中所发送信息的界限,接收或发送的数据可能被TCP协议分割成多个部分
  3. 编写TCP服务器端的程序将在accept方法处阻塞,以等待客户端的连接请求,一旦取得连接,便要为每个客户端创建新的Socket实例进行数据通信
  4. 在 UDP 程序中,创建 DatagramPacket 实例时,如果没有指定远程主机地址和端口,则该实例用来接收数据(尽管可以调用 setXXX()等方法指定),如果指定了远程主机地址和端口,则该实例用来发送数据
  5. UDP 程序在 receive()方法处阻塞,直到收到一个数据报文或等待超时。由于 UDP 协议是不可靠协议,如果数据报在传输过程中发生丢失,那么程序将会一直阻塞在 receive()方法处,这对客户端来说是肯定不行的,为了避免这个问题,我们在客户端使用 DatagramSocket 类的 setSoTimeout()方法来制定 receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端
  6. UDP 服务器为所有通信使用同一套接字,这点与 TCP 服务器不同,TCP 服务器则为每个成功返回的 accept()方法创建一个新的套接字
  7. 在 UDP 程序中,DatagramSocket 的每一次 receive()调用最多只能接收调用一次 send()方法所发送的数据,而且,不同的 receive()方法调用绝对不会返回同一个 send()方法所发送的额数据
  8. 在 UDP 套接字编程中,如果 receive()方法在一个缓冲区大小为 n 的 DatagramPscket 实例中调用,而接受队列中的第一个消息长度大于 n,则 receive()方法只返回这条消息的前 n 个字节,超出的其他字节部分将自动被丢弃,而且也没有任何消息丢失的提示。因此,接受者应该提供一个足够大的缓存空间的 DatagramPacket 实例,以完整地存放调用 receive() 方法时应用程序协议所允许的最大长度的消息。一个 DatagramPacket 实例中所运行传输的最大数据量为 65507 个字节,即 UDP 数据报文所能负载的最多数据,因此,使用一个有 65600 字节左右缓存数组的数据总是安全的

小栗子

客户端

public class Client {
    public Client() {
    }

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        socket.setSoTimeout(3000);
		
		//客户端建立与服务器的连接
        socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), 2000), 3000);
        System.out.println("已发起服务器连接,并进入后续流程~");
        System.out.println("客户端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort());
        System.out.println("服务器信息:" + socket.getInetAddress() + " P:" + socket.getPort());

        try {
            todo(socket);
        } catch (Exception var3) {
            System.out.println("异常关闭");
        }

        socket.close();
        System.out.println("客户端已退出~");
    }

    private static void todo(Socket client) throws IOException {
    	//将输入流包装成缓冲字符流
        InputStream in = System.in;
        BufferedReader input = new BufferedReader(new InputStreamReader(in));
		
		//获取客户端的输出流并包装成打印流
        OutputStream outputStream = client.getOutputStream();
        PrintStream socketPrintStream = new PrintStream(outputStream);
		
		//获取客户端的输入流
        InputStream inputStream = client.getInputStream();
        BufferedReader socketBufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        boolean flag = true;

        do {
        	//获取键盘输入流
            String str = input.readLine();
            //将输入打印给服务器端
            socketPrintStream.println(str);
            String echo = socketBufferedReader.readLine();
            if("bye".equalsIgnoreCase(echo)) {
                flag = false;
            } else {
                System.out.println(echo);
            }
        } while(flag);

        socketPrintStream.close();
        socketBufferedReader.close();
    }
}

服务器端

public class Server {
    public Server() {
    }

    public static void main(String[] args) throws IOException {
    	//服务器监听本机的2000断开
        ServerSocket server = new ServerSocket(2000);
        System.out.println("服务器准备就绪~");
        System.out.println("服务器信息:" + server.getInetAddress() + " P:" + server.getLocalPort());

        while(true) {
        	//获取客户端
            Socket client = server.accept();
            //启动异步线程,处理多客户端
            Server.ClientHandler clientHandler = new Server.ClientHandler(client);
            clientHandler.start();
        }
    }

    private static class ClientHandler extends Thread {
        private Socket socket;
        private boolean flag = true;

        ClientHandler(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            super.run();
            System.out.println("新客户端连接:" + this.socket.getInetAddress() + " P:" + this.socket.getPort());

            try {
            	//获取客户端的输出流
                PrintStream e = new PrintStream(this.socket.getOutputStream());
                BufferedReader socketInput = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

                do {
                	//获取从客户端键盘输入的流,这里是客户端输出到服务器端
                    String str = socketInput.readLine();
                    if("bye".equalsIgnoreCase(str)) {
                        this.flag = false;
                        e.println("bye");
                    } else {
                        System.out.println(str);
                        e.println("回送:" + str.length());
                    }
                } while(this.flag);

                socketInput.close();
                e.close();
            } catch (Exception var12) {
                System.out.println("连接异常断开");
            } finally {
                try {
                    this.socket.close();
                } catch (IOException var11) {
                    var11.printStackTrace();
                }

            }

            System.out.println("客户端已退出:" + this.socket.getInetAddress() + " P:" + this.socket.getPort());
        }
    }
}

报文
  • 报文段在TCP/IP协议网络传输过程,起着路由导航作用
  • 用以查询各个网络路由网段,IP地址,交换协议等IP数据包
  • 报文段充当整个TCP/IP协议数据包的导航路由功能
  • 报文在传输过程中会不断地封装成分组,包,帧来传输
  • 封装方式就是添加一些控制信息组成的首部,即报文头
Mac地址

Mac-Media Access Control-即媒体访问控制,或称为物理地址,硬件地址,用来定义网络设备的位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值