• Socket网络编程


    1、什么是网络编程

          使用IP地址或域名和端口连接到另一台计算机上对应的程序,按照规定的协议(数据格式)来交换数据。

    网络模型图:

     

    2、TCP与UDP在概念上的区别

    UDP:

    1. 面向无连接, 将数据封装成数据包中
    2. 每个数据报的大小在限制64k内
    3. 因无连接,是不可靠协议
    4. 不需要建立连接,速度快

    TCP:

    1. 建立连接,形成传输数据的通道.
    2. 在连接中以字节流方式进行大数据量传输
    3. 通过三次握手完成连接,是可靠协议
    4. 必须建立连接,效率会稍低

    3、UDP通讯案例

         通过UDP协议实现客户端与服务器端进行传输

    package com.zhang.socket;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    
    //socket服务器端
    public  class UdpSocketServer {
    
        public static void main(String[] args) throws IOException {
            System.out.println("udp服务器端启动连接....");
            DatagramSocket datagramSocket=new DatagramSocket(8099);
            byte[] bytes=new byte[1024];
            DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length);
            // 阻塞,等待接受客户端发送请求
            datagramSocket.receive(datagramPacket);
            System.out.println("来源:"+datagramPacket.getAddress()+",端口号:"+datagramPacket.getPort());
            // 获取客户端请求内容
            String string=new String(datagramPacket.getData(),0,datagramPacket.getLength());
            System.out.println(string);
    datagramSocket.close();
    } }
    package com.zhang.socket;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    //客户端
    public class UdpClient {
    
         public static void main(String[] args) throws IOException {
             System.out.println("udp客户端启动连接....");
             DatagramSocket ds = new DatagramSocket();
             String str="哈哈哈";
             byte[] bytes= str.getBytes();
             DatagramPacket dp= new DatagramPacket(bytes, bytes.length, InetAddress.getByName("127.0.0.1"),8099);
             ds.send(dp);
             ds.close();
        }
        
    }

    4、TCP三次握手协议
    第一次握手:建立连接时,客户端发送SYN包(SYN=J)到服务器,并进入SYN_SEND状态,等待服务器确认; 
    第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=J+1),同时自己也发送一个SYN包(SYN=K),即SYN+ACK包,此时服务器V状态; 
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=K+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手,客户端与服务器开始传送数据。

    5、TCP四次分手协议

           由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

    1. 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
    2. 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
    3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
    4. 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

    6、为什么建立连接协议是三次握手,而关闭连接却是四次握手呢

           这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以 未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报 文和FIN报文多数情况下都是分开发送的。

    7、为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态

           这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

    8、TCP通讯案例

    package com.zhang.socket;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    //服务端
    class TcpServer {
    
        public static void main(String[] args) throws IOException {
            System.out.println("socket tcp服务器端启动....");
    
            ServerSocket serverSocket = new ServerSocket(8099);
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            String string = new String(bytes, 0, len);
            System.out.println(string);
            serverSocket.close();
    
        }
    }
    package com.zhang.socket;
    
    import java.io.OutputStream;
    import java.net.Socket;
    
    //客户端
    public class TcpClient {
        public static void main(String[] args) throws Exception {
            System.out.println("socket tcp 客户端启动....");
            Socket socket = new Socket("127.0.0.1", 8099);
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("哈哈哈".getBytes());
            socket.close();
        }
    }

    9、使用多线程支持多个请求

    服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善

    package com.zhang.tcp;
    
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    //tcp服务器端...
    class TcpServer {
    
        public static void main(String[] args) throws Exception {
            System.out.println("socket tcp服务器端启动....");
            ServerSocket serverSocket = new ServerSocket(8099);
            try {
                while (true) {
                    final Socket socket = serverSocket.accept();
                    new Thread(new Runnable() {
                        public void run() {
                            try {
                                InputStream stream = socket.getInputStream();
                                byte[] bytes = new byte[1024];
                                int len = stream.read(bytes);
                                String string = new String(bytes, 0, len);
                                System.out.println("接收到的数据:"+string);
    
                            } catch (Exception e) {
                                e.fillInStackTrace();
                            }
    
                        }
                    }).start();
                }
            } catch (Exception e) {
                e.fillInStackTrace();
            } finally {
                serverSocket.close();
            }
        }
    
    }
    package com.zhang.tcp;
    
    import java.io.OutputStream;
    import java.net.Socket;
    //客户端
    public class TcpClient {
        public static void main(String[] args) throws Exception {
            System.out.println("socket tcp 客户端启动....");
            Socket socket = new Socket("127.0.0.1", 8099);
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("哈哈哈hhhhh".getBytes());
            socket.close();
        }
    }

    利用线程池

    package com.zhang.tcp;
    
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    //tcp服务器端...
    class TcpServer2 {
    
        public static void main(String[] args) throws Exception {
            System.out.println("socket tcp服务器端启动....");
            ExecutorService newCachedThreadPool=Executors.newCachedThreadPool();
            ServerSocket serverSocket = new ServerSocket(8099);
            try {
                while (true) {
                    final Socket socket = serverSocket.accept();
                    newCachedThreadPool.execute(new Runnable() {
                        public void run() {
                            try {
                                InputStream stream = socket.getInputStream();
                                byte[] bytes = new byte[1024];
                                int len = stream.read(bytes);
                                String string = new String(bytes, 0, len);
                                System.out.println("接收到的数据:"+string);
    
                            } catch (Exception e) {
                                e.fillInStackTrace();
                            }
                        }
                    });
                }
            } catch (Exception e) {
                e.fillInStackTrace();
            } finally {
                serverSocket.close();
            }
        }
    
    }

    10、TCP为什么是三次握手,为什么不是两次或者四次

           首先,我们要知道TCP是全双工的,即客户端在给服务器端发送信息的同时,服务器端也可以给客户端发送信息。而半双工的意思是A可以给B发,B也可以给A发,但是A在给B发的时候B不能给A发,即不能同时。 单工为只能A给B发,B不能给A发; 或者是只能B给A发,不能A给B发。

    我们假设A和B是通信的双方。我理解的握手实际上就是通信,发一次信息就是进行一次握手。

    1. 第一次握手: A给B打电话说,你可以听到我说话吗
    2. 第二次握手: B收到了A的信息,然后对A说: 我可以听得到你说话啊,你能听得到我说话吗
    3. 第三次握手: A收到了B的信息,然后说可以的,我要给你发信息啦

    在三次握手之后,A和B都能确定这么一件事: 我说的话,你能听到; 你说的话,我也能听到。 这样,就可以开始正常通信了。

    如果两次,那么B无法确定B的信息A是否能收到,所以如果B先说话,可能后面的A都收不到,会出现问题 。

    如果四次,那么就造成了浪费,因为在三次结束之后,就已经可以保证A可以给B发信息,A可以收到B的信息; B可以给A发信息,B可以收到A的信息。

     

  • 相关阅读:
    maven
    Web开发入门
    网络编程之Socket
    自定义注解与设计模式
    数据交换格式与SpringIOC底层实现
    caffe笔记之例程学习(二)
    caffe笔记之例程学习
    ubuntu14.04 caffe环境配置
    Pattern Recognition (Fourth Edition)读书笔记之mvnrnd函数
    MIF文件编写小技巧
  • 原文地址:https://www.cnblogs.com/zhangjinru123/p/10429816.html
Copyright © 2020-2023  润新知