day40-网络编程02

Java网络编程02

4.TCP网络通信编程

  • 基本介绍
  • 基于客户端–服务端的网络通信
  • 底层使用的是TCP/IP协议
  • 应用场景举例:客户端发送数据,服务端接收并显示控制台
  • 基于Scoket的TCP编程

4.1应用案例

4.1.1应用案例1:(使用字节流)

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在9999端口监听
  3. 客户端连接到服务器端,发送”hello,server”,然后退出
  4. 服务器端接收到客户端发送的信息,输出,并结束

day40-网络编程02

客户端思路:

1.连接服务端(ip,端口)

2.连接上后,生成socket,通过socket.getOutputStream()

3.通过输出流,写入数据到数据通道

package li.network.socket;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

//发送"hello,server"给服务端
public class SocketTCP01Client {
    public static void main(String[] args) throws IOException {
        //1.连接服务端(ip,端口)
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);//连接服务端的主机的端口,在这里是本机
        System.out.println("客户端 socket返回=" + socket.getClass());

        //2.连接上后,生成socket,通过socket.getOutputStream()
        //  得到和 socket 对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();

        //3.通过输出流,写入数据到数据通道
        outputStream.write("hello,server".getBytes());

        //4. 关闭流对象和socket(必须关闭)
        outputStream.close();
        socket.close();

        System.out.println("客户端退出");
    }
}

服务器端思路:

1.在本机的9999端口监听,等待连接

2.当没有客户端连接9999端口时,程序将会阻塞,等待连接

3.通过socket.getInputStream()读取客户端写入到数据通道的数据,显示

package li.network.socket;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketTCP01Server {
    public static void main(String[] args) throws IOException {
        // 1.在本机的9999端口监听,等待连接
        // 要求在本机没有其他服务在监听9999
        // ServerSocket 可以通过 accept() 方法返回多个 Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在9999端口监听,等待连接...");

        // 2.当没有客户端连接9999端口时,程序会阻塞,等待连接
        // 如果有客户端连接则会返回一个socket对象,程序继续

        Socket socket = serverSocket.accept();

        System.out.println("服务器端 socket=" + socket.getClass());

        // 3.通过socket.getInputStream()读取客户端写入到数据通道的数据,显示
        InputStream inputStream = socket.getInputStream();

        // 4.IO读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf, 0, readLen));//根据实际读取到的长度显示内容
        }

        // 5. 关闭流对象和socket(必须关闭)
        inputStream.close();
        socket.close();
        serverSocket.close();//关闭serverSocket
    }
}
  1. 要先运行服务端,这里显示正在等待连接客户端:

day40-网络编程02
  1. 再运行客户端程序,得到一个socket,通过输出流将数据写入到数通道里面,然后退出:

day40-网络编程02
  1. 服务器端会通过输入流不停地读取,把数据输出到控制台上,然后关闭退出:

day40-网络编程02

4.1.2应用案例2:(使用字节流)

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在9999端口监听
  3. 客户端连接到服务器端,发送”hello,server”,并接收服务器端回发的”hello,client”,再退出
  4. 服务器端接收到客户端发送的信息然后输出,并发送”hello,client”,再退出

day40-网络编程02

注意设置写入结束标记 socket.shutdownOutput(); 否则程序会认为还在写入数据。

服务端Server:

package li.network.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

@SuppressWarnings("all")
public class SocketTCP02Server {
    public static void main(String[] args) throws IOException {
        // 1.在本机的9999端口监听,等待连接
        // 要求在本机没有其他服务在监听9999
        // ServerSocket 可以通过 accept() 方法返回多个 Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在9999端口监听,等待连接...");

        // 2.当没有客户端连接9999端口时,程序会阻塞,等待连接
        // 如果有客户端连接则会返回一个socket对象,程序继续

        Socket socket = serverSocket.accept();

        System.out.println("服务器端 socket=" + socket.getClass());

        // 3.获取数据,通过socket.getInputStream()读取客户端写入数据通道的数据,并显示
        InputStream inputStream = socket.getInputStream();
        //IO读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf, 0, readLen));//根据实际读取到的字符串长度显示内容
        }

        // 4. 回话,写入数据
        // 获取socket相关的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello,client".getBytes());
        //设置写入结束标记
        socket.shutdownOutput();

        // 5. 关闭流对象和socket(必须关闭)
        inputStream.close();
        outputStream.close();
        socket.close();
        serverSocket.close();//关闭serverSocket
    }
}

客户端Client:

package li.network.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

@SuppressWarnings("all")
//发送"hello,server"给服务端
public class SocketTCP02Client {
    public static void main(String[] args) throws IOException {
        //1.连接服务端(ip,端口)
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);//连接服务端的主机的端口,在这里是本机
        System.out.println("客户端 socket返回=" + socket.getClass());

        //2.连接上后,生成socket,通过socket.getOutputStream()
        //  得到和 socket 对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();

        //3.通过输出流,写入数据到数据通道
        outputStream.write("hello,server".getBytes());
        //设置写入结束标记
        socket.shutdownOutput();

        //4. 获取数据
        // 获取和socket相关联的输入流,并显示
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf, 0, readLen));
        }

        //5. 关闭流对象和socket(必须关闭)
        outputStream.close();
        inputStream.close();
        socket.close();

        System.out.println("客户端退出");
    }
}
  1. 服务端等待客户端连接:

day40-网络编程02
  1. 客户端往数据通道写入数据”hello,server,并接受到服务端发送的数据”hello,client”

day40-网络编程02
  1. 服务端接收到数据:

day40-网络编程02

4.1.3应用案例3:(使用字符流)

  1. 编写一个服务端,一个客户端
  2. 服务端在9999端口监听
  3. 客户端连接到服务端,发送”hello,server”,并接收到服务端会发的”hello,client”,然后退出
  4. 服务端接收到 客户端发送的信息,输出,并发送”hello,client”,再退出

思路答题与应用案例2一致,这里要求使用字符流,那么在拿到输入/输出字符流之后,使用转换流,将其转换为字符流即可。

PS:设置写入结束标记:除了使用socket.shutdownOutput(); 还可以使用writer.newLine(),如果使用这种结束标志的话,需要使用readLine()的方式读取

注意点:如果使用的是字符流,需要我们手动刷新 xx.flush(); ,否则数据不会写入数据通道

服务端Server:

package li.network.socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

//使用字符流
@SuppressWarnings("all")
public class SocketTCP03Server {
    public static void main(String[] args) throws IOException {
        // 1.在本机的9999端口监听,等待连接
        // 要求在本机没有其他服务在监听9999
        // ServerSocket 可以通过 accept() 方法返回多个 Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在9999端口监听,等待连接...");

        // 2.当没有客户端连接9999端口时,程序会阻塞,等待连接
        // 如果有客户端连接则会返回一个socket对象,程序继续

        Socket socket = serverSocket.accept();

        System.out.println("服务器端 socket=" + socket.getClass());

        // 3.获取数据,通过socket.getInputStream()读取客户端写入数据通道的数据,并显示
        InputStream inputStream = socket.getInputStream();
        //IO读取---使用字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);//输出

        // 4. 回话,写入数据
        // 获取socket相关的输出流
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello,client 字符流");
        bufferedWriter.newLine();//插入一个换行符,表示回复内容的结束
        bufferedWriter.flush();//手动刷新

        // 5. 关闭流对象和socket(必须关闭)
        bufferedWriter.close();//关闭外层流
        bufferedReader.close();
        socket.close();
        serverSocket.close();//关闭serverSocket
    }
}

客户端Client:

package li.network.socket;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

@SuppressWarnings("all")
//发送"hello,server"给服务端,使用字符流
public class SocketTCP03Client {
    public static void main(String[] args) throws IOException {
        //1.连接服务端(ip,端口)
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);//连接服务端的主机的端口,在这里是本机
        System.out.println("客户端 socket返回=" + socket.getClass());

        //2.连接上后,生成socket,通过socket.getOutputStream()
        //  得到和 socket 对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();

        //3.通过输出流,写入数据到数据通道----使用字符流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello,server 字符流");
        bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束,注意:要求对方使用readLine()!!!!

        //如果使用的是字符流,需要我们手动刷新,否则数据不会写入数据通道
        bufferedWriter.flush();

        //4. 获取数据
        // 获取和socket相关联的输入流,并显示
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //5. 关闭流对象和socket(必须关闭)
        bufferedReader.close();//关闭外层流
        bufferedWriter.close();
        socket.close();

        System.out.println("客户端退出");
    }
}

day40-网络编程02day40-网络编程02day40-网络编程02

4.1.4应用案例4:上传图片到服务端

  1. 编写一个服务端和一个客户端
  2. 服务器端在端口8888监听
  3. 客户端连接到服务端,并发送一张图片
  4. 服务器端接收到客户端发送的图片,保存到src目录下,然后发送”收到图片”,再退出
  5. 客户端接收到服务端的回话:”收到图片” 之后,再退出
  6. 该程序要求使用StreamUtils.java

说明:使用BufferedInputStreasm和BufferedOutputStream字节流

思路如下:

  1. 客户端这边先将数据传输到客户端程序中,然后通过socket获取数据通道,将文件数据发送给服务端;
  2. 服务端这边,先是得到数据通道上的数据(传输到服务端程序中),然后使用将程序中的文件数据输出到某个目录下,之后在将”收到图片”的消息输出到数据通道上
  3. 客户端通过socket,从数据通道上获取回复消息,并打印在控制台上

day40-网络编程02

工具类StreamUtils:

package li.network.upload;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 此类用于演示关于流的读写方法
 *
 */
public class StreamUtils {
   /**
    * 功能:将输入流转换成byte[],即可以把文件的内容写入到byte[]
    * @param is
    * @return
    * @throws Exception
    */
   public static byte[] streamToByteArray(InputStream is) throws Exception{
      ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
      byte[] b = new byte[1024];//字节数组
      int len;
      while((len=is.read(b))!=-1){//循环读取
         bos.write(b, 0, len);//把读取到的数据写入到 bos流中
      }
      byte[] array = bos.toByteArray();//然后将bos 转成为一个字节数组
      bos.close();
      return array;
   }

   /**
    * 功能:将InputStream转换成String
    * @param is
    * @return
    * @throws Exception
    */

   public static String streamToString(InputStream is) throws Exception{
      BufferedReader reader = new BufferedReader(new InputStreamReader(is));
      StringBuilder builder= new StringBuilder();
      String line;
      while((line=reader.readLine())!=null){ //当读取到 null时,就表示结束
         builder.append(line+"\r\n");
      }
      return builder.toString();

   }

}

服务端Server:

package li.network.upload;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

//文件上传的服务端
public class TCPFileUploadServer {
    public static void main(String[] args) throws Exception {

        // 1.服务端在监听8888端口(这里是在本机进行)
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在8888端口监听...");
        // 2.等待连接
        Socket socket = serverSocket.accept();

        //3.读取数据通道的数据
        //通过socket获得输入流
        //创建对象
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        //写入到程序中
        byte[] bytes = StreamUtils.streamToByteArray(bis);//streamToByteArray方法:将输入流转换成byte[]

        //4.将得到的bytes数组(文件数据)写入到指定的路径,就得到一个文件
        String destFilePath = "src\\copy.png";
        //创建对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        //写入数据到目录中
        bos.write(bytes);
        bos.close();

        //5.收到图片后,向客户端回复"收到图片"
        //通过socket获得输出流
        //创建对象
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("收到图片");
        bw.flush();//把内容刷新到数据通道
        socket.shutdownOutput();//设置写入结束标记

        //6.关闭其他资源
        bw.close();
        bis.close();
        socket.close();
        serverSocket.close();
    }
}

客户端Client:

package li.network.upload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;

//文件上传的客户端
public class TCPFileUploadClient {
    public static void main(String[] args) throws Exception {
        //1.客户端连接服务端,得到一个socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);

        //2.创建读取磁盘文件的输入流
        String filePath = "d:\\ggmm.jpg";
        //创建对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //这里的 bytes 就是 文件filePath 对应的字节数组
        //读取数据到程序中
        byte[] bytes = StreamUtils.streamToByteArray(bis);

        //3.通过socket获取输出流,将bytes数据发送到服务端
        //创建对象
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        //写入数据通道
        bos.write(bytes);//将文件对应的字节数组写入到数据通道
        bis.close();
        socket.shutdownOutput();//设置写入数据的结束标志

        //4.获取服务端回复的消息
        InputStream inputStream = socket.getInputStream();
        String s = StreamUtils.streamToString(inputStream);//streamToString方法:把一个输入流的数据直接转成字符串
        System.out.println(s);

        //关闭相关的流
        inputStream.close();
        bos.close();
        socket.close();
    }
}

day40-网络编程02 day40-网络编程02

day40-网络编程02 day40-网络编程02

4.2netstat指令

  1. netstat -an 可以查看当前主机的网络情况,包括端口监听情况和网络连接情况
  2. netstat -an | more 可以分页显示
  3. netstat -anb 可以查看当前使用端口的程序
  4. 要求在dos控制台下执行

说明:

(1)Listening 表示某个端口正在监听

(2)如果有一个外部程序(客户端)连接到给端口,就会显示一条连接信息

day40-网络编程02day40-网络编程02day40-网络编程02

4.3TCP连接的秘密

  1. 当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP来分配的,因此是不确定的,是随机的

day40-网络编程02

程序验证:连接时,客户端也会随机分配一个端口

在4.1.4的程序中,将上传的图片改为大数据量的文件传输,方便检测端口情况

  1. 首先启动服务端,可以看到8888端口在监听:

day40-网络编程02
  1. 还未启动客户端的时候,只有服务端8888端口在监听:

day40-网络编程02
  1. 在启动客户端之后,因为这里服务端和客户端都在本机运行,因此可以看到:服务端的端口为8888,客户端随机分配到的端口为4653(如下图:上面是客户端,下面是服务端)

day40-网络编程02

当传输结束之后,客户端的端口将会释放。

Original: https://www.cnblogs.com/liyuelian/p/16705775.html
Author: 一刀一个小西瓜
Title: day40-网络编程02

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/598574/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【数据库】– MySQL SQL调优笔记(1)

    1.索引 1.1.定义 MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。 我们平常所说的索引,如何没有特别的指明,都是B树(多路搜索树,并不…

    数据库 2023年5月24日
    0121
  • 巧用自定义注解,一行代码搞定审计日志

    任何一个软件系统,都不可避免的会碰到【 信息安全】这个词,尤其是对于刚入行的新手,比如我,我刚入行的时候,领导让我做一个数据报表导出功能,我就按照他的意思去做,至于谁有权限操作导出…

    数据库 2023年6月14日
    069
  • nginx反向代理proxy_pass url后加/和不加/的区别

    在nginx中配置proxy_pass反向代理时,当在后面的url加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路…

    数据库 2023年6月6日
    0124
  • 刚入职没多久,连夜手写了一个代码生成器,项目开发速度瞬间屌炸了!

    一、简介 最近刚入职一个新团队,还没来得及熟悉业务,甲方爸爸就要求项目要在2个月内完成开发并上线! 本想着往后推迟1个月在交付,但是甲方爸爸不同意,只能赶鸭子上架了! 然后根据业务…

    数据库 2023年6月14日
    0108
  • CentOS 安装 Docker CE

    CentOS 安装 Docker CE 警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker. 准备工作 系统要求 Docker CE 支…

    数据库 2023年6月6日
    086
  • 2 Java中 == 和 equals 和 hashCode 的区别

    ==是一个比较运算符; 若比较的是基本数据类型,则比较的是值; 若比较的是引用数据类型,则比较的是它们在内存中的内存地址。 说明:对象是存放在堆中,栈中存放的是对象的引用,因此==…

    数据库 2023年6月6日
    099
  • MySQL 中如何归档数据

    归档,在 MySQL 中,是一个相对高频的操作。 它通常涉及以下两个操作: [En] It usually involves the following two actions: …

    数据库 2023年5月24日
    092
  • 在Linux中使用crontab

    查看已存在的任务 查看crontab 输入命令: cat /etc/crontab 在设定编辑之前都建议列出服务查看一下: crontab -l 语法: **** user_nam…

    数据库 2023年6月14日
    088
  • 深入浅出的分析 Set集合

    作者:炸鸡可乐原文出处:www.pzblog.cn 一、摘要 关于 Set 接口,在实际开发中,其实很少用到,但是如果你出去面试,它可能依然是一个绕不开的话题。 言归正传,废话咱们…

    数据库 2023年6月14日
    087
  • SQLZOO练习5–join(表的连接)

    game表: idmdatestadiumteam1team2 1001 8 June 2012 National Stadium, Warsaw POL GRE 1002 8 J…

    数据库 2023年6月16日
    081
  • Java 笔记(全)

    ​ 标识符:给类或者变量类的方法属性命名都是标识符 ​ 组成必须是: 字母、数&#x…

    数据库 2023年6月11日
    077
  • Spring(五)-Spring的其他知识点

    1、细说ServletContext、WebApplicationContext、Servlet的初始化 参考博客–>细说ServletContext、WebAp…

    数据库 2023年6月16日
    0106
  • LeetCode 58. 最后一个单词的长度

    给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1: 输入:…

    数据库 2023年6月11日
    083
  • Django配置日志功能

    Django配置日志功能 LOGGING = { ‘version’: 1, ‘disable_existing_loggers’: False, # 是否禁用已经存在的日志器 ‘…

    数据库 2023年6月14日
    0101
  • MySQL隐式转换的坑

    MySQL以以下规则描述比较操作如何进行转换: 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 实际使用中经研究发现字符串和数字比较时,优先把字符串转换为…

    数据库 2023年6月9日
    075
  • 深入浅出分析 ArrayDeque

    作者:炸鸡可乐原文出处:www.pzblog.cn 一、摘要 在 jdk1.5 中,新增了 Queue 接口,代表一种队列集合的实现,咱们继续来聊聊 java 集合体系中的 Que…

    数据库 2023年6月14日
    093
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球