【网络原理】网络编程Socket套接字基础知识汇总

news/2024/5/18 14:26:19 标签: 网络, udp, 网络协议, tcp/ip, 服务器

目录

1.网络初始:

2.网络编程:

3.UDP数据报套接字:

4.TCP流套接字:


1.网络初始:

  1. 局域网(LAN)广域网(WAN)
  2. IP地址用于定位主机的网络地址。端口号可以标识主机中发送数据接收数据的进程(用于定位主机中的进程)。一个端口只能被一个进程绑定(通常情况),但是一个进程可以绑定多个端口号。
  3. 协议是俩个人的事情,只有双方都了解并且遵守才有意义!
  4. 协议分层,上层协议调用下层协议,下层协议给上层协议提供服务,相邻的层是可以相互交互的,但是不能跨层级。
  5. TCP/IP五层网络模型和封装,
    假设主机A给主机B发了个helloworld
    主机A发送的过程
    
    一.应用层
       应用程序会把输入的helloworld构造成约定好的应用层协议的报文
       应用程序就会把这个应用数据报文,交给传输层协议
       传输层是操作系统内核实现的,操作系统提供了一些API给应用程序,
       这些API叫做socket api,代码调用这些api就可以把应用层的数据交给传输层(交给了操作系统内核)
    
    二.传输层
       传输层这里有很多协议,最典型的就是TCP协议,此处以TCP为例
       TCP协议要在之前的基础上,加上个TCP的协议报头
       这个TCP报头里面最重要的就是源端口和目的端口!
       传输层继续将这个数据交给网络层进行处理
            
    三.网络
       网络层中最典型的就是IP协议
       IP协议把整个TCP数据看成整体,作为载荷部分,在前头加上IP协议报头
       IP协议报头里面有很多信息,最关键的就是源IP和目的IP
       构造好IP数据报之后,IP协议继续把整个数据交给数据链路层
            
    四.数据链路层
       数据链路层的协议有很多,最典型的就是以太网
       以太网这个歌协议既管数据链路层,又管物理层
       以太网数据帧将IP数据报的前头加上帧头(源mac地址和目的mac地址),后头加上帧尾(校验和)
    
    五.物理层
       到达物理层的数据已经组织好了
       就可以通过物理层设备(网卡)把上述数据的二进制bit流转换成光信号或电信号来传输
  6. 分用就是封装的逆过程,封装是打包快递,而分用就是拆开快递。
  7. 网络中的细节太多了,如果一个协议搞定,那这个协议就会非常的复杂。因此就需要拆分,拆分的多了,又要分层。拆分之后,一个协议负责一件事情,这样才把这些关键信息放到了不同的协议报头中。

2.网络编程:

  1. 网络编程指的是网络上的主机,通过不同的进程,以编程的方式实现网络通信网络编程主要是针对应用层。
  2. 网络编程套接字,就是研究如何写代码完成网络编程。socket api是一切网络编程的基础。
  3. socket套接字是操作系统给应用程序提供的API,描述的是应用层和传输层的交互其实socket api就是传输层给应用层提供的
  4. API就是一组类和方法。应用程序就可以通过socket api来进行网络编程(操作网卡)。
  5. 网络传输层中又很多种协议,最主要的就是TCP和UDP。因此操作系统就提供了俩个不同的版本的API。
  6. 传输层中TCP和UDP的区别
    TCPUDP
    有连接无连接
    可靠传输不可靠传输
    面向字节流面向数据报
    全双工全双工
  7. TCP和文件操作一样是基于“流”的,而UDP则是以“数据报”为基本单位。全双工是一个通道,双向通信;半双工是一个通道,单向通信。网络通信一般都是全双工的。有连接就相当于打电话,必须通信的双方建立好了连接才可以正常打电话;而无连接相当于发短信,直接就可以发送过去。

3.UDP数据报套接字:

  1. DatagramSocketDatagramPacket是UDP socket需要掌握的类。
  2. DatagramSocket,是网卡的代言人,借助这个类来读写网卡。通过网卡发送数据就是写文件,接收数据就是读文件。
    方法签名说明
    DatagramSocket()一般用于客户端,创建一个UDP数据报套接字的socket,绑定到随机一个端口
    DatagramSocket(int port)一般用于服务器,创建一个UDP数据报套接字的socket,绑定到指定端口
    void receive(DatagramPacket p )接收数据报,没有收到会阻塞等待
    void send(DatagramPacket p )发送数据报,不会阻塞等待
    void close()关闭数据报套接字
  3. socket本质上是一个文件。socket对应到网卡这个硬件设备,操作系统也是把网卡当作文件来管理。通过网卡发送数据,就是写文件;通过网卡接收数据,就是读文件。
  4. DatagramPacket,代表一个UDP数据包,是一次发送/接受的基本单位;发送和接收的是DatagramPacket。
    方法签名说明
    DatagramPacket(byte[ ] b,int length)构造一个DatagramPacket用来接收数据报,接收的数据保存在字节数组里
    DatagramPacket(byte[ ] b,int offset,int length,address)构造一个DatagramPacket用来发送数据报,发送的数据为字节数组的指定长度。address为指定目的主机的IP和端口号
    getAddress()从接收的数据报中获取发送端主机IP地址;或从发送的数据报中获取接收端主机IP地址
    int getPort()从接收的数据报中获取发送端主机端口号;或从发送的数据报中获取接收端主机端口号
    byte[ ] getData()获取数据报中的数据
  5. UDP实现回显服务器服务器部分)
    //UDP版本:回显服务器服务器部分
    
    public class UdpEchoServer {
        private DatagramSocket socket = null;
    
        //参数的端口表示服务器要绑定的端口
        //不需要指定IP,就是本机的IP
        public UdpEchoServer(int port) throws SocketException {
            socket = new DatagramSocket(port);
        }
    
        //启动服务器
        public void start() throws IOException {
            System.out.println("服务器启动了!!!");
            while(true){
                //1.读取请求并且解析
                //socket的receive操作需要一个空的requestPacket,receive方法的参数是一个输出型参数
                //将空的DatagramPacket对象交给receive,在receive里面负责把从网卡读到的数据填充到这个对象中
                DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);//要给DatagramPacket申请内存空间
                socket.receive(requestPacket);
                //将DatagramPacket转换成字符串    getData()是获取数据报中的数据,返回一个byte[]
                String request = new String(requestPacket.getData(),0, requestPacket.getLength());
    
                //2.根据请求计算响应
                String response = process(request);
    
                //3.把响应返回给客户端
                //发送DatagramPacket对象需要指定IP地址和端口号
                DatagramPacket responsePacket = new DatagramPacket(    //getSocketAddress就是得到客户端的IP和端口号
                        response.getBytes(),response.getBytes().length, requestPacket.getSocketAddress());
                socket.send(responsePacket);
    
                //4.打印一个日志
                System.out.printf("[%s %d]: req: %s ; resp: %s\n",
                        requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
            }
        }
    
        public String process(String request) {
            return request;
        }
    
        public static void main(String[] args) throws IOException {
            UdpEchoServer server = new UdpEchoServer(1025);
            server.start();
        }
    }

  6. UDP实现回显服务器(客户端部分)
    //UDP版本:回显服务器的客户端部分
    
    public class UdpEchoClient {
        private DatagramSocket socket = null;
        private String serverIP;
        private int serverPort;
        //服务器的IP一般不用写,就是本机的IP
        //需要传服务器的IP和服务器的端口
        public UdpEchoClient(String serverIp,int serverPort) throws SocketException {
            socket = new DatagramSocket();//不用指定参数
            this.serverIP = serverIp;
            this.serverPort = serverPort;
        }
    
        public void start() throws IOException {
            Scanner scanner = new Scanner(System.in);
            while(true){
                //1.从控制台上读取用户输入的内容
                System.out.print("-> ");
                String request = scanner.nextLine();
    
                //2.构造一个UDP请求,发送给服务器
                DatagramPacket requestPacket = new DatagramPacket(
                        //request.getBytes().length这里的length单位是字节
                        //request.length()这里的length()单位是字符,不可以改成这样
                        request.getBytes(),request.getBytes().length, InetAddress.getByName(this.serverIP),this.serverPort);
                socket.send(requestPacket);
    
                //3.从服务器接收响应,并且转成字符串
                DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
                socket.receive(responsePacket);
                String response = new String(responsePacket.getData(),0, responsePacket.getLength());
    
                //4.把响应显示到控制台上
                System.out.println(response);
            }
        }
    
        public static void main(String[] args) throws IOException {
            //IP是某某食堂,端口号是某某窗口
            UdpEchoClient client = new UdpEchoClient("127.0.0.1",1025);
            client.start();
        }
    }
    

  7. DatagramPacket的三种构造方法
  8. 为什么服务器需要指定端口,而客户端不用指定端口???答:服务器指定端口目的就是方便客户端找到服务器在哪而客户端不指定端口因为操作系统会分配一个空闲的端口,如果手动指定了万一用户电脑上的其他程序占用了这个端口,就会导致程序无法正确运行了。
  9. 对于服务器来说,读取请求并且解析根据请求计算响应把响应写回到客户端执行的速度是极快的。如果有多个客户端同时发来请求,服务器也是可以响应的,但是本质上这三个请求的串行处理的!
  10. 当前俩个程序放在同一个主机上是通过127.0.0.1这个IP来通信的;也可以把俩个程序放在不同的主机上也是可以通信的,但是如果放在不同的主机上,要确保服务器的地址是可以访问到的!

4.TCP流套接字:

  1. ServerSocketSocket是TCP流套接字需要掌握的俩个类。
  2. ServerSocket是创建TCP服务器的api,服务器使用的类,用来监听端口
    方法签名说明
    ServerSocket(int port)创建一个服务端流套接字Socket,并且绑定指定端口
    Socket accept()有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接否则阻塞等待
    void close()关闭此套接字
  3. Socket既会给服务器使用也会给客户端使用,用来传输数据。
    方法签名说明
    Socket(String host,int port)创建一个客户端流套接字Socket,并且尝试和对应IP的主机上对应端口的进程建立连接
    InetAddress getInetAddress()返回套接字所连接的地址,获取IP和端口
    int getPort()返回套接字所连接的端口
    InputStream  getInputStream()返回此套接字的输入流
    OutputStream getOutputStream()返回此套接字的输出流
  4. TCP实现回显服务器服务器部分)
    //TCP版本:回显服务器服务器部分
    
    public class TcpEchoServer {
        private ServerSocket listenSocket = null;
    
        public TcpEchoServer(int port) throws IOException {
            listenSocket = new ServerSocket(port);
        }
    
        public void start() throws IOException {
            System.out.println("服务器启动!!");
            //使用线程池
            ExecutorService service = Executors.newCachedThreadPool();
            while(true) {
                //1.先调用accept
                Socket clientSocket = listenSocket.accept();
    
    
                //2.再来处理这个连接,这里应该使用多线程,每个客户端连上来都分配一个新的线程负责处理
                //使用多线程确实可以解决问题,但是会频繁的创建和销毁线程!
    //            Thread t = new Thread(()->{
    //                try {
    //                    processConnection(clientSocket);
    //                } catch (IOException e) {
    //                    throw new RuntimeException(e);
    //                }
    //            });
    //            t.start();
    
                service.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            processConnection(clientSocket);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    
        private void processConnection(Socket clientSocket) throws IOException {
            System.out.printf("[%s:%d] 客户端上线!\n",
                    clientSocket.getInetAddress().toString(),clientSocket.getPort());
    
            //处理客户端的请求
            //clientSocket代表的是服务器的网卡,inputStream代表从网卡读数据也就相当于从客户端读取数据
            try(InputStream inputStream = clientSocket.getInputStream();
                OutputStream outputStream = clientSocket.getOutputStream()){
                while(true){
                    //1.读取请求并且解析
                    Scanner scanner = new Scanner(inputStream);
                    if(!scanner.hasNext()){
                        System.out.printf("[%s:%d] 客户端下线!\n",
                                clientSocket.getInetAddress().toString(),clientSocket.getPort());
                        break;
                    }
                    String request = scanner.next();
    
                    //2.根据请求计算响应
                    String response = process(request);
    
                    //3.把响应写回到客户端
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(response);
                    //刷新缓冲区确保数据确实是通过网卡发送出去了
                    printWriter.flush();
    
                    System.out.printf("[%s:%d] req: %s; resp: %s\n",
                            clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                //为什么这个clientSocket要关闭文件,前面的listenSocket和UDP程序中的socket都不需要关闭文件呢?
                clientSocket.close();
            }
        }
        public String process(String request) {
            return request;
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoServer server = new TcpEchoServer(9090);
            server.start();
        }
    }

  5. TCP实现回显服务器(客户端部分)
    //TCP版本:回显服务器客户端部分
    
    public class TcpEchoClient {
        private Socket socket = null;
    
        public TcpEchoClient(String serverIp, int serverPort) throws IOException {
            socket = new Socket(serverIp, serverPort);
        }
    
        public void start() {
            Scanner scanner = new Scanner(System.in);
    
            try (InputStream inputStream = socket.getInputStream();
                 OutputStream outputStream = socket.getOutputStream()) {
                while (true) {
                    //1.从控制台读取数据
                    System.out.print("-> ");
                    String request = scanner.next();
    
                    //2.发送请求给服务器
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(request);
                    printWriter.flush();
    
                    //3.从服务器上接收响应
                    Scanner respScanner = new Scanner(inputStream);
                    String response = respScanner.next();
    
                    //4.把响应显示到界面上
                    System.out.println(response);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoClient server = new TcpEchoClient("127.0.0.1",9090);
            server.start();
        }
    
    }

  6. TCP实现回显服务器服务器代码部分,如果使用1的话,就不能让多个客户端同时使用服务器;如果使用2,频繁的创建和销毁代价较大,所以推荐使用线程池!
  7. TCP实现回显服务器服务器代码中需要close文件!
  8. 启动服务器,如果没有客户端建立连接,服务器就会阻塞等待。如果有一个客户端过来了,此时就会显示客户端已上线并且向下执行代码。如果再有一个客户端也过来了,使用上述的方案1那么不会显示这个客户端已上线
  9. TCP中的长短连接。TCP发送数据时需要先建立连接,什么时候关闭连接就决定是短连接还是长连接。短连接:每次收到数据并返回响应后,都关闭连接,也就是短连接只能一次收发数据,TCP每个连接只处理一个客户端请求能够保证快速调用到accept。长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,也就是长连接可以多次收发数据,TCP建立连接之后,要处理客户端的多次请求才导致无法快速调用accept。


如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹


http://www.niftyadmin.cn/n/2236.html

相关文章

HTML之表格、表单

用 <table> <tr> <td>单元格</td> </tr> </table> 可以创建一个最简单的只有一行、一个单元格的表格。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>HTML表格…

c++ queue用法 入门必看 超详细

1、queue的作用 说到queue&#xff0c;大家一定会想到stack&#xff0c;同样是简单易用的数据结构之一。queue就是队列的意思&#xff0c;像大家日常排队一样&#xff0c;先排的人先用。stack则是相反的&#xff0c;后来的先用。这就有了queue先进先出&#xff0c;stack后进先…

CMake中include的使用

CMake中的include命令用于从文件或模块(file or module)加载并运行CMake code。其格式如下&#xff1a; include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>][NO_POLICY_SCOPE]) 从给定的文件加载并运行CMake code。变量读写访问调用者的范围(Variable rea…

SpringBoot整合Javamail实现邮件发送

博客主页&#xff1a;踏风彡的博客 博主介绍&#xff1a;一枚在学习的大学生&#xff0c;希望在这里和各位一起学习。 所属专栏&#xff1a;SpringBoot学习笔记 文章创作不易&#xff0c;期待各位朋友的互动&#xff0c;有什么学习问题都可在评论区留言或者私信我&#xff0c;我…

宁德时代换挡,钠电池“接力”锂电池?

宁德时代以一己之力将钠电池推上了台前&#xff0c;钠电池量产被提上日程&#xff0c;钠离子电池产业化将进一步加速。 去年7月&#xff0c;宁德时代在发布会上宣布&#xff0c;其开发的第一代钠离子电池&#xff0c;电芯单体能量密度已经达到了160Wh/kg&#xff0c;为目前全球…

Java连接mysql数据库

文章目录一、Java连接mysql数据库1.1 流程1.2 一个测试连接的java程序二、优化&#xff1a;创建一个工具类2.1 存在的问题2.2 创建配置文件和工具类2.3 测试使用工具类进行CRUD操作一、Java连接mysql数据库 1.1 流程 java连接mysql大致需要这六步&#xff1a; 导入驱动包&am…

【uniapp小程序开发】—— 组件封装之【自定义弹窗】

文章目录&#x1f34b;前言&#xff1a;&#x1f34d;正文1、探讨需求封装popup自定义弹窗组件2、实战开发弹窗组件2.1 子组件内容 popup.vue文件2.2 父组件引用子组件3、效果图预览3.1 不使用具名插槽的原有样式效果3.2 使用具名插槽之后样式效果&#x1f383;专栏分享&#x…

从1-10,中国制造业数字化持续增长的路径与实践

3月30日&#xff08;周三&#xff09;下午&#xff0c;纷享销客[对话|新增长100]栏目的制造行业专场如约而至&#xff0c;本次也是纷享销客2022年B2B新增长趋势系列的第三期&#xff0c;话题聚焦“从1-10&#xff0c;中国制造业数字化持续增长的路径及实践? ” 纷享销客制造行…