• day40-网络编程02


    Java网络编程02

    4.TCP网络通信编程

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

    4.1应用案例

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

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

    image-20220918165642571

    客户端思路:

    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. 要先运行服务端,这里显示正在等待连接客户端:
    image-20220918165750028
    1. 再运行客户端程序,得到一个socket,通过输出流将数据写入到数通道里面,然后退出:
    image-20220918165823508
    1. 服务器端会通过输入流不停地读取,把数据输出到控制台上,然后关闭退出:
    image-20220918165840071

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

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

    注意设置写入结束标记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. 服务端等待客户端连接:
    image-20220918174438250
    1. 客户端往数据通道写入数据“hello,server,并接受到服务端发送的数据“hello,client”
    image-20220918174502483
    1. 服务端接收到数据:
    image-20220918174555801

    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("客户端退出");
    }
    }
    image-20220918183512374 image-20220918183527148 image-20220918183544508

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

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

    说明:使用BufferedInputStreasm和BufferedOutputStream字节流

    思路如下:

    1. 客户端这边先将数据传输到客户端程序中,然后通过socket获取数据通道,将文件数据发送给服务端;

    2. 服务端这边,先是得到数据通道上的数据(传输到服务端程序中),然后使用将程序中的文件数据输出到某个目录下,之后在将“收到图片”的消息输出到数据通道上

    3. 客户端通过socket,从数据通道上获取回复消息,并打印在控制台上

    工具类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();
    }
    }

    image-20220918202653847 image-20220918202750982

    image-20220918202841610 image-20220918202833159

    4.2netstat指令

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

    说明:

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

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

    image-20220919154312169 image-20220919154326099 image-20220919155558316

    4.3TCP连接的秘密

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

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

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

    1. 首先启动服务端,可以看到8888端口在监听:
    image-20220919161455622
    1. 还未启动客户端的时候,只有服务端8888端口在监听:
    image-20220919161526355
    1. 在启动客户端之后,因为这里服务端和客户端都在本机运行,因此可以看到:服务端的端口为8888,客户端随机分配到的端口为4653(如下图:上面是客户端,下面是服务端)
    image-20220919162526936

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

  • 相关阅读:
    CocosCreator3.8研究笔记(十四)CocosCreator 资源管理Asset Manager
    php 常用的接口和函数
    C语言——数组详解
    selenium安装和python中基本使用
    linux中awk命令有何作用?
    DiskSim 4.0安装详细流程(基于Ubuntu14 32位系统)
    LeetCode --- 1460. Make Two Arrays Equal by Reversing Subarrays 解题报告
    MySQL 定义条件与处理程序 的详细讲解
    Go 使用mencached缓存
    C++ 入门14:STL 容器之向量(vector)
  • 原文地址:https://www.cnblogs.com/liyuelian/p/16705775.html