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 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> TCP通信程序 -> 正文阅读

[网络协议]TCP通信程序

一、概述

TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。

  • 两端通信时步骤:

    1. 服务端程序,需要事先启动,等待客户端的连接。
    2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
  • 在Java中,提供了两个类用于实现TCP通信程序:

    1. 客户端:java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
    2. 服务端:java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。

二、Socket类

TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据

  • Socket 类:此类实现客户端套接字(也可以就叫“套接字”)。
  • 套接字是两台机器间通信的端点。
  • 套接字:包含了 IP 地址端口号的网络单位 。

2.1 构造方法

  • public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。

    小贴士:回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。

    构造举例,代码如下:

    Socket client = new Socket("127.0.0.1", 8888);
    

2.2 成员方法

  • public InputStream getInputStream() : 返回此套接字的输入流。
    • 如果此 Scoket 具有相关联的通道,则生成的 InputStream 的所有操作也关联该通道。
    • 关闭生成的 InputStream 也将关闭相关的 Socket
  • public OutputStream getOutputStream() : 返回此套接字的输出流。
    • 如果此 Scoket 具有相关联的通道,则生成的 OutputStream 的所有操作也关联该通道。
    • 关闭生成的 OutputStream 也将关闭相关的Socket。
  • public void close() :关闭此套接字。
    • 一旦一个 socket 被关闭,它不可再使用。
    • 关闭此 socket 也将关闭相关的 InputStreamOutputStream
  • public void shutdownOutput() : 禁用此套接字的输出流。
    • 任何先前写出的数据将被发送,随后终止输出流。

2.3 实现步骤

  1. 创建一个客户端对象 Socket ,构造方法绑定服务器的IP地址和端口号
  2. 使用 Socket 对象中的方法 getOutputStream() 获取网络字节输出流 OutputStream 对象
  3. 使用网络字节输出流 OutputStream 对象中的方法 write,给服务器发送数据
  4. 使用 Socket 对象中的方法 getInputStream() 获取网络字节输入流 InputStream 对象
  5. 使用网络字节输入流 InputStream 对象中的方法 read,读取服务器回写的数据
  6. 释放资源 (Socket)

注意:

  1. 客户端和服务器端进行交互,必须使用 Socket 中提供的网络流,不能使用自己创建的流对象
  2. 当我们创建客户端对象 Socket 的时候,就会去请求服务器,和服务器经过3次握手建立连接通路
    • 这时如果服务器没有启动,那么就会抛出异常 ConnectException: Connection refused: connect
    • 如果服务器已经启动,那么就可以进行交互了

三、ServerSocket类

TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据

ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。

3.1 构造方法

  • public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。

    构造举例,代码如下:

    ServerSocket server = new ServerSocket(8888);
    

3.2 成员方法

服务器端必须明确一件事情,必须的知道是哪个客户端请求的服务器,所以可以使用 accept 方法获取到请求的客户端对象 Socket

  • public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。

3.3 实现步骤

  1. 创建服务器 ServerSocket 对象和系统要指定的端口号
  2. 使用 ServerSocket 对象中的方法 accept ,获取到请求的客户端对象 Socket
  3. 使用 Socket 对象中的方法 getInputStream() 获取网络字节输入流 InputStream 对象
  4. 使用网络字节输入流 InputStream 对象中的方法 read ,读取客户端发送的数据
  5. 使用 Socket 对象中的方法 getOutputStream() 获取网络字节输出流 OutputStream 对象
  6. 使用网络字节输出流 OutputStream 对象中的方法 write ,给客户端回写数据
  7. 释放资源(Socket,ServerSocket)

四、简单的TCP网络程序

4.1 TCP通信分析图解

  1. 【服务端】启动,创建 ServerSocket 对象,等待连接。
  2. 【客户端】启动,创建 Socket 对象,请求连接。
  3. 【服务端】接收连接,调用 accept 方法,并返回一个 Socket 对象。
  4. 【客户端】Socket 对象,获取 OutputStream ,向服务端写出数据。
  5. 【服务端】Scoket 对象,获取 InputStream ,读取客户端发送的数据。

到此,客户端向服务端发送数据成功。

5_简单通信

自此,服务端向客户端回写数据。

  1. 【服务端】Socket 对象,获取 OutputStream ,向客户端回写数据。
  2. 【客户端】Scoket 对象,获取 InputStream ,解析回写数据。
  3. 【客户端】释放资源,断开连接。

4.2 TCP网络程序

服务端实现:

public class ServerTCP {
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动 , 等待连接 .... ");
        // 1.创建 ServerSocket对象,绑定端口,开始等待连接
        ServerSocket ss = new ServerSocket(8888);
        // 2.接收连接 accept 方法, 返回 socket 对象.
        Socket server = ss.accept();
        // 3.通过socket 获取输入流
        InputStream is = server.getInputStream();
        // 4.一次性读取数据
      	// 4.1 创建字节数组
        byte[] b = new byte[1024];
      	// 4.2 据读取到字节数组中.
        int len = is.read(b);
        // 4.3 解析数组,打印字符串信息
        String msg = new String(b, 0, len);
        System.out.println(msg);
      	// =================回写数据=======================
      	// 5. 通过 socket 获取输出流
      	 OutputStream out = server.getOutputStream();
      	// 6. 回写数据
      	 out.write("我很好,谢谢你".getBytes());
      	// 7.关闭资源.
      	out.close();
        is.close();
        server.close();
    }
}

客户端实现:

public class ClientTCP {
	public static void main(String[] args) throws Exception {
		System.out.println("客户端 发送数据");
		// 1.创建 Socket ( ip , port ) , 确定连接到哪里.
		Socket client = new Socket("localhost", 8888);
		// 2.通过Scoket,获取输出流对象 
		OutputStream os = client.getOutputStream();
		// 3.写出数据.
		os.write("你好么? tcp ,我来了".getBytes());
      	// ==============解析回写=========================
      	// 4. 通过Scoket,获取 输入流对象
      	InputStream in = client.getInputStream();
      	// 5. 读取数据数据
      	byte[] b = new byte[100];
      	int len = in.read(b);
      	System.out.println(new String(b, 0, len));
		// 6. 关闭资源 .
      	in.close();
		os.close();
		client.close();
	}
}

结果如图
在这里插入图片描述
在这里插入图片描述

五、练习–上传文件

5.1 C/S基础版本–单线程

  • 服务器端实现

    读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"

    public class TCPServer {
        public static void main(String[] args) throws IOException {
            //1.创建一个服务器ServerSocket对象,和系统要指定的端口号
            ServerSocket server = new ServerSocket(8888);
            //2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
            Socket socket = server.accept();
            //3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
            InputStream is = socket.getInputStream();
            //4.判断d:\\upload文件夹是否存在,不存在则创建
            File file =  new File("D:\\upload");
            if(!file.exists()){
                file.mkdirs();
            }
    
    
            //5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
            FileOutputStream fos = new FileOutputStream(file+"\\1.jpg");
            //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
    
            System.out.println("11111111111111111111");
    
            int len =0;
            byte[] bytes = new byte[1024];
            while((len = is.read(bytes))!=-1){
                //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
                fos.write(bytes,0,len);
            }
    
            System.out.println("22222222222222222222222  while死循环打印不到");
    
            //8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
            //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
            socket.getOutputStream().write("上传成功".getBytes());
            //10.释放资源(FileOutputStream,Socket,ServerSocket)
            fos.close();
            socket.close();
            server.close();
        }
    }
    
  • 客户端实现

    读取本地文件,上传到服务器,读取服务器回写的数据

    public class TCPClient {
        public static void main(String[] args) throws IOException {
            //1.创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
            FileInputStream fis = new FileInputStream("D:\\1.jpg");
            //2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
            Socket socket = new Socket("127.0.0.1",8888);
            //3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
            OutputStream os = socket.getOutputStream();
            //4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
            int len = 0;
            byte[] bytes = new byte[1024];
            while((len = fis.read(bytes))!=-1){
                //5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
                os.write(bytes,0,len);
            }
    
            /*
                解决:上传完文件,给服务器写一个结束标记
                void shutdownOutput() 禁用此套接字的输出流。
                对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
             */
            socket.shutdownOutput();
    
            //6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
            InputStream is = socket.getInputStream();
    
            System.out.println("333333333333333333333");
    
            //7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
            while((len = is.read(bytes))!=-1){
                System.out.println(new String(bytes,0,len));
            }
    
            System.out.println("444444444444444444  while死循环打印不到");
    
            //8.释放资源(FileInputStream,Socket)
            fis.close();
            socket.close();
        }
    }
    

5.2 C/S 升级版本–多线程

客户端实现不变,服务器实现如下:

public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.创建一个服务器ServerSocket对象,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        //2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象

        /*
            让服务器一直处于监听状态(死循环accept方法)
            有一个客户端上传文件,就保存一个文件
         */
        while(true){
            Socket socket = server.accept();

            /*
                使用多线程技术,提高程序的效率
                有一个客户端上传文件,就开启一个线程,完成文件的上传
             */
            new Thread(new Runnable() {
                //完成文件的上传
                @Override
                public void run() {
                   try {
                       //3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
                       InputStream is = socket.getInputStream();
                       //4.判断d:\\upload文件夹是否存在,不存在则创建
                       File file =  new File("d:\\upload");
                       if(!file.exists()){
                           file.mkdirs();
                       }

                    /*
                        自定义一个文件的命名规则:防止同名的文件被覆盖
                        规则:毫秒值+随机数
                     */
                       String fileName = System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

                       //5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
                       //FileOutputStream fos = new FileOutputStream(file+"\\1.jpg");
                       FileOutputStream fos = new FileOutputStream(file+"\\"+fileName);
                       //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件


                       int len =0;
                       byte[] bytes = new byte[1024];
                       while((len = is.read(bytes))!=-1){
                           //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
                           fos.write(bytes,0,len);
                       }


                       //8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
                       //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
                       socket.getOutputStream().write("上传成功".getBytes());
                       //10.释放资源(FileOutputStream,Socket,ServerSocket)
                       fos.close();
                       socket.close();
                   }catch (IOException e){
                       System.out.println(e);
                   }
                }
            }).start();


        }

        //服务器就不用关闭
        //server.close();
    }
}

5.3 B/S 升级版本

客户端浏览器,web代码就不粘贴了,详情已放到下载区–java进阶代码11-Net
服务器实现

public class TCPServer {
    public static void main(String[] args) throws IOException {
        //创建一个服务器ServerSocket,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8080);
        //使用accept方法获取到请求的客户端对象(浏览器)
        Socket socket = server.accept();
        //使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息
        /*byte[] bytes = new byte[1024];
        int len = 0;
        while((len = is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }*/

        //把is网络字节输入流对象,转换为字符缓冲输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        //把客户端请求信息的第一行读取出来 GET /11_Net/web/index.html HTTP/1.1
        String line = br.readLine();
        //把读取的信息进行切割,只要中间部分 /11_Net/web/index.html
        String[] arr = line.split(" ");
        //把路径前边的/去掉,进行截取 11_Net/web/index.html
        String htmlpath = arr[1].substring(1);

        //创建一个本地字节输入流,构造方法中绑定要读取的html路径
        FileInputStream fis = new FileInputStream(htmlpath);
        //使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();

        // 写入HTTP协议响应头,固定写法
        os.write("HTTP/1.1 200 OK\r\n".getBytes());
        os.write("Content-Type:text/html\r\n".getBytes());
        // 必须要写入空行,否则浏览器不解析
        os.write("\r\n".getBytes());

        //一读一写复制文件,把服务读取的html文件回写到客户端
        int len = 0;
        byte[] bytes = new byte[1024];
        while((len = fis.read(bytes))!=-1){
            os.write(bytes,0,len);
        }

        //释放资源
        fis.close();
        socket.close();
        server.close();
    }
}

图解
在这里插入图片描述
但是运行客户端浏览器发现图片不显示,经下面修改后,图片显示正常

public class TCPServerThread {
    public static void main(String[] args) throws IOException {
        //创建一个服务器ServerSocket,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8080);

        /*
            浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片
            我们就的让服务器一直处于监听状态,客户端请求一次,服务器就回写一次
         */
        while(true){
            //使用accept方法获取到请求的客户端对象(浏览器)
            Socket socket = server.accept();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
                        InputStream is = socket.getInputStream();
                        //使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息
                        /*byte[] bytes = new byte[1024];
                        int len = 0;
                        while((len = is.read(bytes))!=-1){
                            System.out.println(new String(bytes,0,len));
                        }*/

                        //把is网络字节输入流对象,转换为字符缓冲输入流
                        BufferedReader br = new BufferedReader(new InputStreamReader(is));
                        //把客户端请求信息的第一行读取出来 GET /11_Net/web/index.html HTTP/1.1
                        String line = br.readLine();
                        System.out.println(line);
                        //把读取的信息进行切割,只要中间部分 /11_Net/web/index.html
                        String[] arr = line.split(" ");
                        //把路径前边的/去掉,进行截取 11_Net/web/index.html
                        String htmlpath = arr[1].substring(1);

                        //创建一个本地字节输入流,构造方法中绑定要读取的html路径
                        FileInputStream fis = new FileInputStream(htmlpath);
                        //使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
                        OutputStream os = socket.getOutputStream();

                        // 写入HTTP协议响应头,固定写法
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());
                        os.write("Content-Type:text/html\r\n".getBytes());
                        // 必须要写入空行,否则浏览器不解析
                        os.write("\r\n".getBytes());

                        //一读一写复制文件,把服务读取的html文件回写到客户端
                        int len = 0;
                        byte[] bytes = new byte[1024];
                        while((len = fis.read(bytes))!=-1){
                            os.write(bytes,0,len);
                        }

                        //释放资源
                        fis.close();
                        socket.close();
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();


        }

        //server.close();
    }
}
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-19 12:24:57  更:2021-08-19 12:25:22 
 
开发: 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/25 20:27:04-

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