IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 网络编程之套接字 -> 正文阅读

[系统运维]网络编程之套接字

目录

为什么需要网络编程

什么是网络编程

传输层的两个重要协议

什么是套接字Socket

分类

Java使用UDP协议?

?Java使用TCP协议?

两个协议的对比?


为什么需要网络编程

为什么需要网络编程? —— 丰富的网络资源
用户在浏览器中,打开在线视频网站,如优酷看视频,实质是通过网络,获取到网络上的一个视频资源。 与本地打开视频文件类似,只是视频文件这个资源的来源是网络。 相比本地资源来说,网络提供了更为丰富的网络资源:

?所谓的网络资源,其实就是在网络中可以获取的各种数据资源。 而所有的网络资源,都是通过网络编程来进行数据传输的

什么是网络编程

什么是网络编程
网络编程,指网络上的主机,通过 不同的进程 ,以编程的方式实现 网络通信(或称为网络数据传输)

  • 当然,我们只要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据, 也属于网络编程。
  • 特殊的,对于开发来说,在条件有限的情况下,一般也都是在一个主机中运行多个进程来完成网络编程。 但是,我们一定要明确,我们的目的是提供网络上不同主机,基于网络来传输数据资源:
  • 进程A:编程来获取网络资源
  • 进程B:编程来提供网络资源
  • 一般意义上的网络通信,一般站在应用层视角去探讨,所以一般的意义上的网络编程,多是在传输层上进行讨论

传输层的两个重要协议

  1. UDP User Datagram Protocol 用户报文协议 UDP没有做任何处理,保存网络的原生态,是不可靠,无连接,面向数据报文的一种协议
  2. TCP Transmission Control Protocol 传输控制协议 可靠,有连接,面向字节流的一种协议
  3. 都是传输层协议,都需要实现进程到进程的通信,都是站在应用层的角度

网络层的协议是IP协议

如何建立一条网络上(逻辑上)的通信线路

发送端和接收端
在一次网络数据传输时:
  • 发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
  • 接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
  • 收发端:发送端和接收端两端,也简称为收发端。
注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。
请求和响应
一般来说,获取一个网络资源,涉及到两次网络数据传输:
  • 第一次:请求数据的发送
  • 第二次:响应数据的发送。

客户端和服务端

  • 服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
  • 客户端获取服务的一方进程,称为客户端。

对于服务来说

  • 一般是提供:客户端获取服务资源
  • 客户端保存资源在服务端

好比在银行办事:
  • 银行提供存款服务:用户(客户端)保存资源(现金)在银行(服务端)
  • 银行提供取款服务:用户(客户端)获取服务端资源(银行替用户保管的现金)

什么是套接字Socket

概念
Socket 套接字,是由系统提供用于网络通信的技术,是基于 TCP/IP 协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程

?

分类

流套接字 :使用传输层 TCP 协议
  1. 有连接
  2. 可靠传输
  3. 面向字节流
  4. 有接收缓冲区,也有发送缓冲区
  5. 大小不限
对于字节流来说,可以简单的理解为,传输数据是基于 IO 流,流式数据的特征就是在 IO 流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。
数据报套接字 :使用传输层 UDP 协议
  1. 无连接
  2. 不可靠传输
  3. 面向数据报
  4. 有接收缓冲区,无发送缓冲区
大小受限:一次最多传输 64k
对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如 100 个字节,必须一次发送,接收也必须一次接收100 个字节,而不能分 100 次,每次接收 1 个字节
Java数据报套接字通信模型

?对于一次发送及接收UDP数据报的流程如下:

?提供多个客户端的请求处理及响应,流程如下

Java使用UDP协议?

套接字的创建

DatagramSocket 构造方法

  • ?UDP服务器(Server)采用一个固定端口,方便客服端进行通信,可能会有错误的风险,(该端口已经被其他进程占用)
  • UDP客户端(Client),不需要采用固定端口(可以采用)
DatagramSocket 一些常用方法

  • ?一旦通信的双方逻辑意义上有了通信线路,双方地位是平等的(谁都可以作为接收方,谁都可以作为发送方)
  • 通信结束后,都需要进进行资源回收
  • 对于接收方法,如果没有接收到一个数据报,进程会一直阻塞

数据报的创建

  • 数据报是通信过程的数据抽象,就理解成通信过程中发送/接收一个信封
  • 作为接收方,只需要提供存放数据的位置
  • 作为发送方,将需要发送的数据,和要发送给谁(远端ip+端口)

数据包类的方法

  • 对于服务器使用,来获得客户端的ip和port
  • 对于接收者:拿到信的内容,对方进程发送的应用层的数据

?例子 基于UDP的翻译服务器和客户端

服务端

package com.lsc.net_demo.udp;

import com.lsc.net_demo.util.Log;

import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

public class TranslateServer {
    private static final HashMap<String,String> map=new HashMap<>();
    //公开的ip地址,就看进程在哪个ip上
    //公开的port,需要在程序中指定
    public static  final int PORT=8888;
    public static void main(String[] args) throws IOException {
        Log.println("准备进行字典的初始化");
        initMap();
        Log.println("完成字典的初始化");
        Log.println("准备创建UDPSocket,端口是"+PORT);
        DatagramSocket socket=new DatagramSocket(PORT);
        Log.println("UDP Socket创建成功");
        //作为服务器,是被动的,循环的进行请求,响应周期的处理
        //等待处理,处理并发送响应,直到永远
        while (true){
            //1接收请求
            byte []buf=new byte[1024];//1024 代表我们最大接收数据大小
            DatagramPacket receivePacket=new DatagramPacket(buf, buf.length);
            Log.println("准备好接收DatagramPacket,最大大小为"+buf.length+"字节");
            Log.println("开始接收请求");
            socket.receive(receivePacket);//这个方法会阻塞,程序执行到这就不动了,除非有客户发送请求,才能继续
            //2一旦走到此处,说明接收到了请求,拆信
            //拆出对方的IP地址
            InetAddress address=receivePacket.getAddress();
            Log.println("对方的IP地址"+address);
            //拆出对方的端口
            int port=receivePacket.getPort();
            Log.println("对方的端口"+port);
            //拆出对方ip+端口
            SocketAddress socketAddress=receivePacket.getSocketAddress();
            Log.println("对象的完整地址"+socketAddress);
            //拆出对方发送的数据,其实这个data就是刚才定义的buf数组
            byte[]data =receivePacket.getData();
            Log.println("刚接收的对象的数据"+ Arrays.toString(data));
            //拆出接收到的数据的大小
            int length=receivePacket.getLength();
            Log.println("接收的数据大小(字节)"+length);
            //3解析请求,意味着我们需要定义字节的应用层协议
            //首先将字符集解码
            String request=new String(data,0,length,"UTF-8");
            String engWorld=request;
            Log.println("请求(英文单词)"+engWorld);
            //执行业务,进行翻译
            String chiWorld=translate(engWorld);
            //按照应用层协议,封装响应
            String response=chiWorld;
            //进行字符集编码 String->byte[]
            byte []sendBuf=response.getBytes("UTF-8");
            //发送响应
            //作为发送方需要提供数据报的类
            DatagramPacket sentPacket=new DatagramPacket(sendBuf,0,sendBuf.length,//要发送的数据
                    socketAddress//从请求中拆出的对象地址
            );
            Log.println("准备好发送DatagramPacket并发送");
            socket.send(sentPacket);
            Log.println("发送成功");
            //本次请求-回应周期完成,继续下一次请求

        }
       // socket.close();
    }

    private static String translate(String engWorld) {
        String chiWorld=map.getOrDefault(engWorld,"查无此单词");
        return chiWorld;
    }

    private static void initMap() {
        map.put("apple","苹果");
        map.put("pear","梨子");
        map.put("orange","橙子");
    }
}

客户端

package com.lsc.net_demo.udp;

import com.lsc.net_demo.util.Log;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class UserInputLoopClient {
    public static void main(String[] args) throws IOException {
        Scanner sc=new Scanner(System.in);
        //1创建UDPSocket
        Log.println("准备创建UDP socket");
        DatagramSocket socket=new DatagramSocket();
        Log.println("UDP socket创建结束");
        System.out.println("请输入英文单词:");
        while (sc.hasNextLine()) {
            //2发送请求
            String engWorld = sc.nextLine();
            Log.println("英文单词是" + engWorld);
            String request = engWorld;
            byte[] bytes = request.getBytes("UTF-8");
            //手段构造服务器的地址
            //现在我们的客服端和服务端都在一台主机,所以使用127.0.0.1
            InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
            //端口使用Translate.PORT(8888)
            DatagramPacket sentPacket = new DatagramPacket(bytes, 0, bytes.length,
                    loopbackAddress,//对方ip
                    TranslateServer.PORT//对方的端口
            );
            Log.println("准备发送请求");
            socket.send(sentPacket);
            Log.println("请求发送结束");
            //3接收响应
            byte[] buf = new byte[1024];
            DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
            Log.println("准备接收响应");
            socket.receive(receivePacket);
            byte[] data = receivePacket.getData();
            int len = receivePacket.getLength();
            String response = new String(data, 0, len, "UTF-8");
            String chiWord = response;
            Log.println("翻译结果" + chiWord);
            System.out.println("请输入英文单词:");
        }
        socket.close();
    }

}

?Java使用TCP协议?

服务端的Socket建立

  • 有连接(先拨号,拨通才能通信) 无连接 接发信(无脑发送,对方在不在都没关系)
  • 因为TCP是有连接的,服务器使用TCP Socket(传入的端口就是要公开的端口,一般称为监听端口)

服务端ServerSocket的方法

  • 服务器的Socket(客户端对象)是通过accept中获取来的,所以客户端Socket对象需要主机手动实例
  • 挂电话,谁都可以挂

?客户端的Socket建立

  • 一旦拿到socket对象拿到,双方是同时拿到的(电话是同时解通的),双方的地位就平等了,只需要分发送方和/接收方

?客户端的Socket方法

  • 拿到对方的IP地址/getRemote()拿到对方的端口
  • 输入流:站在进程角度,是输入流(背后对象就是网卡,网卡抽象出来的TCP连接),所以是给接收方使用的
  • 输出流:所以是给发送方方使用的

?输入和输出流的使用

因为是基于字节流的,所以可以使用输入输出流

  • 网络编程的输入输出流是站在进程的角度,所以输入是进程输入给别的对象,所以是给接收方使用,输出是别的东西输出给进程,所以是发送方使用
  • 对于TCP来说,是基于字节流的,所以我们可以对输入流进行封装,封装成Scanner使用,对于输出流,先封装成OutputStream然后在封装成PrintWriter进行使用
  • 对于输出的话,因为是要输出东西给进程, 所以需要刷新缓冲区,flush
TCP 中的长短连接
TCP 发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
  1. 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数 据。
  2. 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以 多次收发数据

例子

  • ?短连接,就是我每次说一句话,就挂断电话,如果想继续发信息,需要重新打电话
  • 长连接,就是打通电话,我把我想说的全部都说了,一句接着一句(我说一句,回应一句)

?关于同时在线的服务器和客户端

  1. 短连接客户端和短连接服务器 支持同时在线,因为短连接是说一句就挂了,和服务器的连接也断了(其他客户端可以申请服务),所以可以支持多个连接
  2. 短链接客户端和长连接服务器,虽然是长连接服务器,但是是短连接客户端,客户端要求一次服务,就断开连接了(相当于我可以一直说话,但是我就是愿意说一句就挂断),跟前面的一种情况一致
  3. 长连接客户端和长连接服务器,就构成一个电话我可能会一直打,所以服务器一直被占用,其他长连接的 客户端就不能接收服务,不能支持同时在线,怎么解决呢?通过多线程解决,将提供服务的功能让子线程完成,主线程就只管服务端和客户端之间关系建立

例子 基于TCP翻译服务器和客户端(短链接)

服务器

package com.lsc.net_demo.tcp;

import com.lsc.net_demo.util.Log;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;

/**
 * 短连接就算每次发送一个信息,就会挂掉电话
 */
public class TranslateServerShortConnection {
    public static final int PORT = 8888;

    public static void main(String[] args) throws Exception {
        initMap();
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (true) {
            // 接电话
            Log.println("等待对方来连接");
            Socket socket = serverSocket.accept();//会阻塞 socket是来自客户端的连接
            Log.println("有客户端连接上来了");

            // 对方信息:
            InetAddress inetAddress = socket.getInetAddress();  // ip
            Log.println("对方的 ip: " + inetAddress);
            int port = socket.getPort();    // port
            Log.println("对方的 port: " + port);
            SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();    // ip + port
            Log.println("对方的 ip + port: " + remoteSocketAddress);

            // 读取请求
            InputStream inputStream = socket.getInputStream();
            Scanner scanner = new Scanner(inputStream, "UTF-8");
            String request = scanner.nextLine();    // nextLine() 就会去掉换行符
            String engWord = request;
            Log.println("英文: " + engWord);

            // 翻译
            String chiWord = translate(engWord);
            Log.println("中文: " + chiWord);

            // 发送响应
            String response = chiWord;  // TODO: 响应的单词中是没有 \r\n
            OutputStream outputStream = socket.getOutputStream();
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
            PrintWriter writer = new PrintWriter(outputStreamWriter);
            Log.println("准备发送");
            writer.printf("%s\r\n", response);
            writer.flush();
            Log.println("发送成功");

            // 挂掉电话 关闭在循环里面,说明每次发送一个信息就会关闭连接
            socket.close();
            Log.println("挂断电话");
        }
//        serverSocket.close();
    }


    private static final HashMap<String, String> map = new HashMap<>();

    private static void initMap() {
        map.put("apple", "苹果");
        map.put("pear", "梨");
        map.put("orange", "橙子");
    }

    private static String translate(String engWord) {
        String chiWord = map.getOrDefault(engWord, "查无此单词");
        return chiWord;
    }
}

客户端

package com.lsc.net_demo.tcp;

import com.lsc.net_demo.util.Log;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class UserInputLoopConnectionClient {
    //短连接
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("请输入英文单词");
            if(!scanner.hasNextLine()){
                break;
            }
            String engWord = scanner.nextLine();
            //直接创建Socket 使用服务器IP+PORT
            Log.println("准备创建socket(TCP连接)");
            Socket socket = new Socket("127.0.0.1", TranslateServerShortConnection.PORT);
            Log.println("创建socket(TCP连接)成功创建");
            //发送请求
            Log.println("英文" + engWord);
            String request = engWord + "\r\n";
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osWriter = new OutputStreamWriter(os);
            PrintWriter pw = new PrintWriter(osWriter);
            Log.println("准备发生请求");
            pw.print(request);
            pw.flush();
            Log.println("发送请求成功");
            //等待响应接收
            InputStream is = socket.getInputStream();
            Scanner sc = new Scanner(is, "UTF-8");
            Log.println("准备接收请求");
            String chiWord = sc.nextLine();
            Log.println("接收请求成功");
            Log.println("中文" + chiWord);
            socket.close();//每次发送一个信息,都会关闭连接 然后循环重新连接
        }
    }

}

长链接

服务器(带多线程解决不是同时在线问题)

package com.lsc.net_demo.tcp;

import com.lsc.net_demo.util.Log;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Scanner;

public class TranslateServerLongConnectionPool {
    static class 专门负责处理接电话的人 implements Runnable{
        Socket socket;
        public 专门负责处理接电话的人(Socket socket){
            this.socket=socket;
        }
        @Override
        public void run() {
            try {
                // 对方信息:
                InetAddress inetAddress = socket.getInetAddress();  // ip
                Log.println("对方的 ip: " + inetAddress);
                int port = socket.getPort();    // port
                Log.println("对方的 port: " + port);
                SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();    // ip + port
                Log.println("对方的 ip + port: " + remoteSocketAddress);
                //初始化
                //用来接收信息
                InputStream inputStream = socket.getInputStream();
                Scanner scanner = new Scanner(inputStream, "UTF-8");
                //用来发送信息
                OutputStream outputStream = socket.getOutputStream();
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
                PrintWriter writer = new PrintWriter(outputStreamWriter);
                while (true) {
                    if(!scanner.hasNextLine()) {
                        Log.println("对方挂电话了");
                        //代表scanner背后的InputStream遇到了EOS(-1)
                        //代表客户端挂电话了
                        //所以服务端也挂电话
                        break;
                    }
                    // 读取请求
                    String request = scanner.nextLine();    // nextLine() 就会去掉换行符
                    String engWord = request;
                    Log.println("英文: " + engWord);

                    // 翻译
                    String chiWord = translate(engWord);
                    Log.println("中文: " + chiWord);

                    // 发送响应
                    String response = chiWord;  // TODO: 响应的单词中是没有 \r\n
                    Log.println("准备发送");
                    writer.printf("%s\r\n", response);
                    writer.flush();
                    Log.println("发送成功");
                }
                // 挂掉电话
                socket.close();
                Log.println("挂断电话");
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public static final int PORT = 8888;
    public static void main(String[] args) throws Exception {
        Log.println("启动长链接版本的TCP服务器");
        initMap();
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (true) {
            // 接电话
            Log.println("等待对方来连接");
            Socket socket = serverSocket.accept();//会阻塞 socket是来自客户端的连接
            Log.println("有客户端连接上来了");

           Runnable task=new 专门负责处理接电话的人(socket);
           //把任务交给专门的线程处理
            new Thread(task).start();

        }
//        serverSocket.close();
    }


    private static final HashMap<String, String> map = new HashMap<>();

    private static void initMap() {
        map.put("apple", "苹果");
        map.put("pear", "梨");
        map.put("orange", "橙子");
    }

    private static String translate(String engWord) {
        String chiWord = map.getOrDefault(engWord, "查无此单词");
        return chiWord;
    }
}

客户端

package com.lsc.net_demo.tcp;

import com.lsc.net_demo.util.Log;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class UserInputLoopLongConnectionClient {
    public static void main(String[] args) throws IOException {
        //长链接模式下,只拨通一次电话
        //直接创建Socket 使用服务器IP+PORT
        Scanner scanner=new Scanner(System.in);
        Log.println("准备创建socket(TCP连接)");

        Socket socket=new Socket("127.0.0.1",TranslateServerShortConnection.PORT);
        Log.println("创建socket(TCP连接)成功创建");
        while (true) {
            System.out.print("请输入英文单词");
            if(!scanner.hasNextLine()){
                break;
            }
            String engWord = scanner.nextLine();
            String request = engWord + "\r\n";
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osWriter = new OutputStreamWriter(os);
            PrintWriter pw = new PrintWriter(osWriter);
            Log.println("准备发生请求");
            pw.print(request);
            pw.flush();
            Log.println("发送请求成功");
            //等待响应接收
            InputStream is = socket.getInputStream();
            Scanner sc = new Scanner(is, "UTF-8");
            Log.println("准备接收请求");
            String chiWord = sc.nextLine();
            Log.println("接收请求成功");
            Log.println("中文" + chiWord);
        }
        socket.close();//只有在输入多个信息完毕之和,才会关闭连接

    }

}

两个协议的对比?

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:20:33  更:2022-07-20 19:21:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 12:11:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码