Java 网络编程 模拟TCP通信的代码实现 ; 4 个项目的练习题 ; 解决read()阻塞问题。

一.通信的IO流了解

在这里插入图片描述

  1. 客户端和服务端用IO流交互
  2. 服务端使用客户端的IO流进行交互

二、练习1.实现TCP的相互通信代码实现

1. 客户端

Client用户.java

package Java学习.网络编程.TCP_UDP.TCP通信代码实现;

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

/**
 * 一、学习的2个类:
 * 1.Client套接字: Socket 创建Socket 对象,发出OutputStream 请求 ,服务端回复后,InputStream 收到回复
 * 创建Socket(String IP ,String port) 时候会自动和服务端Server3次握手建立联系
 * 2.Server 服务套接字: new ServerSocket(port) 开启服务等待连接
 * 二、步骤
 * 1.服务器新建ServerSocket对象,打开端口,
 * 2..accept()获取Socket开始监听客户端的请求
 * 3.客户端新建Socket,指定IP和端口连接服务端
 * 4.客户端使用getOutputStream 新建一个写入流准备写入字节到服务端
 * 5.服务端结接收到打印信息回复客户端
 * 6.客户端手接受来自服务端的信息,并打印
 * 7.客户端关闭,Socket()
 * 8.服务端关闭获取的Socket,然后关闭 ServerSocket
 */
public class Client用户 {
    public static void main(String[] args) throws IOException {
        //3.客户端新建Socket
        Socket localhost = new Socket("127.0.0.1", 8888);
        //4.客户端使用getOutputStream 新建一个写入流准备写入字节到服务端
        OutputStream outputStream = localhost.getOutputStream();
        //5 客户端调用outputStream.write()写入数据到服务端
        outputStream.write("客户端:调用outputStream.write()写入数据到服务端".getBytes());
        //6.客户端手接受来自服务端的信息,并打印
        InputStream inputStream = localhost.getInputStream();
        byte[] bytes2 = new byte[1024];
        int length = inputStream.read(bytes2,0,bytes2.length);
        System.out.println(new String(bytes2,0,length));
        //7.客户端关闭,Socket()
        localhost.close();
        /**
         * 问题:为什么接受不到服务端发送的信息呢???
         */
    }
}


2. 服务端

Server服务端.java

package Java学习.网络编程.TCP_UDP.TCP通信代码实现;

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

/**
 * 一、学习的2个类:
 * 1.Client套接字: Socket 创建Socket 对象,发出OutputStream 请求 ,服务端回复后,InputStream 收到回复
 * 创建Socket(String IP ,int port) 时候会自动和服务端Server3次握手建立联系
 * 2.Server 服务套接字: new ServerSocket(port) 开启服务等待连接
 * 二、步骤
 * 1.服务器新建ServerSocket对象,打开端口,
 * 2..accept()获取Socket开始监听客户端的请求
 * 3.客户端新建Socket,指定IP和端口连接服务端
 * 4.客户端使用getOutputStream 新建一个写入流准备写入字节到服务端
 * 5.服务端结接收到打印信息回复客户端
 * 6.客户端手接受来自服务端的信息,并打印
 * 7.客户端关闭,Socket()
 * 8.服务端关闭获取的Socket,然后关闭 ServerSocket
 */
public class Server服务器 {
    public static void main(String[] args) throws IOException {
        //1.服务器新建ServerSocket对象,打开端口,
        ServerSocket serverSocket = new ServerSocket(8888);
        //2.accept()获取Socket开始监听客户端的请求
        Socket accept = serverSocket.accept();
        //5.服务端结接收到打印信息回复客户端
        InputStream inputStream = accept.getInputStream();
        byte[] bytes = new byte[1024*10];
        int length;
        while ((length = inputStream.read(bytes,0,bytes.length)) != -1){
            System.out.println(new String(bytes,0,length));
        }
        OutputStream outputStream = accept.getOutputStream();
        outputStream.write("服务器: 已经收到信息".getBytes());
        outputStream.flush();
        //8.服务端关闭获取的Socket,然后关闭 ServerSocket
        accept.close();
        serverSocket.close();


    }
}

3.操作步骤,先启动服务端,再启动用户端.

4. Run结果

在这里插入图片描述
在这里插入图片描述

问题??为什么服务端给用户的回信看不到????

1.原因

在这里插入图片描述

2. 解决方案.

在这里插入图片描述

三、练习2,

模拟TCP传输,
新增要求:
1.规范异常处理
2.使用管道流避免1024*n字节刚好断开字符导致的乱码的问题

1. Client客户端.java

package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习2新增规范异常处理和管道流;

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

/**
 * 一、步骤
 * 1.客户端: new Socket(String ip,int port)
 * 2.客户端: getOutputStream 然后发送信息给服务端Server
 * 3.服务端 new ServerSocket(int port )
 * 4.服务端 getInputStream客户端的读取数据,要
 * 求读取时使用管道流字节数组输出流.ByteArraysOutputStream防止乱码
 */
public class Client客户端 {
    public static void main(String[] args) {
        Socket socket = null;
        OutputStream outputStream = null;
        try {
            //1.客户端: new Socket(String ip,int port)
            socket = new Socket("127.0.0.1", 8887);
            //2.客户端: getOutputStream 然后发送信息给服务端Server
            outputStream = socket.getOutputStream();
            outputStream.write("客户端: 发送请求信息".getBytes());
        } catch (IOException e) {

        } finally {
            //先开后关 (1)
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //先开后关(2)
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.Server服务端.java

package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习2新增规范异常处理和管道流;

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

/**
 * 一、步骤
 * 1.客户端: new Socket(String ip,int port)
 * 2.客户端: getOutputStream 然后发送信息给服务端Server
 * 3.服务端 new ServerSocket(int port ) 开放端口,并 accept() 获得 客户端的Socket()
 * 4.服务端 getInputStream客户端的读取数据,要
 * 求读取时使用管道流字节数组输出流.ByteArraysOutputStream防止乱码
 * 二、注意原则,
 * 1. 流先开后关,
 * 2. 变量的声明和定义分离. ServerSocket serverSocket = null;
 * 3.finally 用于关闭流,进行判断 if ( io != null) io.close()
 */
public class Server服务端 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream inputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            //3.服务端 new ServerSocket(int port ) 开放端口,并 accept() 获得 客户端的Socket()
            serverSocket = new ServerSocket(8887);
            socket = serverSocket.accept();
            //4.服务端 getInputStream客户端的读取数据,要
            inputStream = socket.getInputStream();
            byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024 * 10];
            int length;
            while ((length = inputStream.read(bytes, 0, bytes.length)) != -1) {
                byteArrayOutputStream.write(bytes, 0, length);
            }
            byte[] bytes1 = byteArrayOutputStream.toByteArray();
            System.out.println("收到的信息为: " + (new String(bytes1)));
        } catch (IOException e) {

        } finally {
            //先开后关(1)
            if (byteArrayOutputStream != null) {
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //先开后关(2)
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //先开后关(3)
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //先开后关(4)
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

先打开服务端,再打开客户端,看代码。
Run:
在这里插入图片描述

四、练习3,TCP的文件的传输

1.目的:

  • 一、目的:做一个服务器,实现
  • 上传文件_服务器一直开_文件不覆盖_多线程

2.分析

  • 二、步骤
  • 1.客户端Socket 连接 服务端
  • 2.客户端使用本地流,读取文件,使用网络流上传文件
    1. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
  • 4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
  • 5.客户端接受上传成功的提示.
  • 二、注意规范,
    1. 流先开后关,
    1. 变量的声明和定义分离. ServerSocket serverSocket = null;
  • 3.finally 用于关闭流,进行判断 if ( io != null) io.close()

3.code

Client客户端3.java

package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习3上传文件_服务器一直开_文件不覆盖_多线程;

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

/**
 * 一、目的:做一个服务器,实现
 * 上传文件_服务器一直开_文件不覆盖_多线程
 * 二、步骤
 * 1.客户端Socket 连接 服务端
 * 2.客户端使用本地流,读取文件,使用网络流上传文件
 * 3. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
 * 4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
 * 5.客户端接受上传成功的提示.
 * 二、注意规范,
 * 1. 流先开后关,
 * 2. 变量的声明和定义分离. ServerSocket serverSocket = null;
 * 3.finally 用于关闭流,进行判断 if ( io != null) io.close()
 */
public class Client客户端3 {
    public static void main(String[] args) {

        //声明定义分离
        Socket socket = null;
        FileInputStream fileInputStream = null;
        OutputStream outputStream = null;
        try {
            //1.客户端Socket 连接 服务端
            socket = new Socket("127.0.0.1", 8887);
            //2.客户端使用本地流,读取文件,使用网络流上传文件
            String url = "D:\\Program Files\\JetBrains\\test1\\Lab\\src\\Java学习\\网络编程\\TCP_" +
                    "UDP\\TCP通信代码实现\\练习3上传文件_服务器一直开_文件不覆盖_多线程\\";
            fileInputStream = new FileInputStream(url + "img.png");
            outputStream = socket.getOutputStream();
            byte[] bytes = new byte[1024 * 10];
            while (fileInputStream.read(bytes) != -1) {
                outputStream.write(bytes);
            }
            //传输完成之后关闭网络输出流
            socket.shutdownOutput();
            //5.客户端接受上传成功的提示.
            InputStream inputStream = socket.getInputStream();
            //使用管道流防止字符乱码
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] bytes1 = new byte[1024];
            int length;
            while ((length = inputStream.read(bytes1)) != -1) {
                byteArrayOutputStream.write(bytes1, 0, length);
            }
            System.out.println("收到回复:" + (new String(byteArrayOutputStream.toByteArray())));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //先开后关(1)
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //先开后关(2)
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //先开后关(3)
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }


    }
}

Server服务端3.java

package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习3上传文件_服务器一直开_文件不覆盖_多线程;

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

/**
 * 一、目的:做一个服务器,实现
 * 上传文件_服务器一直开_文件不覆盖_多线程
 * 二、步骤
 * 1.客户端Socket 连接 服务端
 * 2.客户端使用本地流,读取文件,使用网络流上传文件
 * 3. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
 * 4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
 * 5.客户端接受上传成功的提示.
 * 二、注意规范,
 * 1. 流先开后关,
 * 2. 变量的声明和定义分离. ServerSocket serverSocket = null;
 * 3.finally 用于关闭流,进行判断 if ( io != null) io.close()
 */
public class Server服务端3 {
    public static void main(String[] args) {
        //声明定义分离1
        ServerSocket serverSocket = null;
        Socket socket = null;
        String url = "D:\\Program Files\\JetBrains\\test1\\Lab\\src\\Java学习\\网络编程\\TCP_UDP\\TCP" +
                "通信代码实现\\练习3上传文件_服务器一直开_文件不覆盖_多线程\\imgs";
        try {
            //3. 服务端while(true)执行,如果accept()到一个Socket连接,新建一个线程,
            serverSocket = new ServerSocket(8887);
            System.out.println("服务端: 服务端启动成功");
            while (true) {
                socket = serverSocket.accept();
                if (socket != null) {
                    Socket socketExist = socket;
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            //新建一个线程
                            //声明定义分离 2
                            InputStream inputStream = null;
                            String fileName = null;
                            BufferedOutputStream bufferedOutputStream = null;
                            OutputStream outputStream = null;
                            try {
                                //4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
                                File file = new File(url);
                                if (!file.exists()) {//如果文件夹不存在.
                                    file.mkdirs();//创建文件夹
                                }
                                inputStream = socketExist.getInputStream();
                                fileName = "\\" + socketExist.getInetAddress() + socketExist.getPort() + System.currentTimeMillis()+(new Random().nextInt(99999)) + ".png";
                                //网络流的读取,读取本地使用BufferOutputStream
                                bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(url + fileName));
                                byte[] bytes = new byte[1024 * 100];
                                int length;
                                while ((length = inputStream.read(bytes)) != -1) {
                                    bufferedOutputStream.write(bytes, 0, length);
                                }
                                System.out.println("服务端: 接受成功 "+fileName);
                                //并返回上传成功的提示
                                outputStream = socketExist.getOutputStream();
                                outputStream.write("服务端:您已经上传成功!".getBytes());

                            } catch (IOException e) {
                                e.printStackTrace();
                            } finally {
                                //先进后出(2.1)
                                if (outputStream != null) {
                                    try {
                                        outputStream.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                                //先进后出(2.2)
                                if (bufferedOutputStream != null) {
                                    try {
                                        bufferedOutputStream.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                                //先进后出(2.3)
                                if (inputStream != null) {
                                    try {
                                        inputStream.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }

                        }
                    }).start();
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //先进后出(1.1)
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //先进后出(1.2)
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

Run:
(先打开服务端,再打开客户端,服务端不会停止,客户端可多次上传文件)
(Server服务端3的窗口)
服务端: 服务端启动成功
服务端: 接受成功 /127.0.0.111785158907230368282203.png
服务端: 接受成功 /127.0.0.111794158907231279174305.png

(Client客户端3的窗口)
收到回复:服务端:您已经上传成功!

Process finished with exit code 0
在这里插入图片描述
(过程展示)
在这里插入图片描述

五、练习4 BS TCP交互实现

1.目的

  • 一、目的:做一个服务器,实现
  • 上传文件_服务器一直开_文件不覆盖_多线程
  • 客户端换为浏览器端

2.思路

  • 一、步骤
    1. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
    1. 新线程,接受信息,提取用用访问的文件的路径信息,
    1. 使用获取的页面的路径,是用本地流读取,使用HTTP协议网络流写入输出到浏览器,返回给客户端页面
  • @HTTP协议 固定写法
  •  outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
    
  •  outputStream.write("Content-Type:text/html\r\n".getBytes());
    

//必须写入空行,不然浏览器不解析

  •  outputStream.write("\r\n".getBytes());
    
  • 二、问题:
    1. 浏览器需要刷新2次服务端才能得到信息.访问成功????,
  • 首次输入连接不行,关闭连接或者刷新才能得到提交的信息,
  • 删除while read,一次接受文件,会一次打印3次,提交的信息????
  • 删除持续服务器的while true 无法打印连接后直接退出???

3.代码:


package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习4浏览器客户端;

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

/**
 * 一、步骤
 * 1. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
 * 2. 新线程,接受信息,提取用用访问的文件的路径信息,
 * 3. 使用获取的页面的路径,是用本地流读取,使用HTTP协议网络流写入输出到浏览器,返回给客户端页面
 * @HTTP协议 固定写法
 *      outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
 *      outputStream.write("Content-Type:text/html\r\n".getBytes());
 *      //比如写入空行,不然浏览器不解析
 *      outputStream.write("\r\n".getBytes());
 *
 * 二、问题:
 * 1. 浏览器需要刷新2次服务端才能得到信息.访问成功????,
 * 首次输入连接不行,关闭连接或者刷新才能得到提交的信息,
 * 删除while read,一次接受文件,会一次打印3次,提交的信息????
 * 删除持续服务器的while true 无法打印连接后直接退出???
 */
public class Server服务端4 {
    public static void main(String[] args) {
        //声明和定义分离 1
        ServerSocket serverSocket = null;
        Socket accept = null;

        try {

            serverSocket = new ServerSocket(8888);
            System.out.println("服务端已经启动:" + serverSocket);
            /**
             * 1. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
             */
            while (true) {
                accept = serverSocket.accept();

                System.out.println("连接成功! -- " + accept);
                //新线程
                if (accept != null) {
                    Socket finalAccept = accept;
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            /**
                             * 2. 新线程,接受信息,提取用用访问的文件的路径信息,
                             * 提取用用访问的文件的路径信息:
                             * (1). BufferReader.readLine() 可读取第一行的有用信息。
                             * 构造 new BufferReader(new InputStreamReader(finalAccept.accept()))
                             * 因为只有 InputStreamReader 才能接受 网络流的InputStream 变为为
                             * 可被BufferReader 构造的 Reader
                             */
                            //声明定义分离 2
                            InputStream inputStream = null;
                            BufferedReader bufferedReader = null;

                            try {
                                inputStream = finalAccept.getInputStream();
                                //提取有用信息
                                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                                String getUrl = bufferedReader.readLine().split(" ")[1];
                                String baseUrl ="D:/Program Files/JetBrains/test1/Lab";
                                String finalUrl = baseUrl +getUrl;
                                /**
                                 * 3. 使用获取的页面的路径,是用本地流读取,
                                 * 使用HTTP协议将:网络流写入输出到浏览器,返回给客户端页面
                                 * HTTP协议响应头固定写法:
                                 *
                                 */
                                FileInputStream fileInputStream = new FileInputStream(finalUrl);
                                OutputStream outputStream = finalAccept.getOutputStream();
                                /**
                                 * HTTP 响应头
                                 */
                                outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
                                outputStream.write("Content-Type:text/html\r\n".getBytes());
                                //比如写入空行,不然浏览器不解析
                                outputStream.write("\r\n".getBytes());

                                byte[] bytes = new byte[1024 * 20];
                                int length = 0;
                                while ((length = fileInputStream.read(bytes))!= -1){
                                    outputStream.write(bytes,0,length);
                                }


                            } catch (IOException e) {

                            } finally {
                                //(2.1)
                                if (bufferedReader != null) {
                                    try {
                                        bufferedReader.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                                //(2.2)
                                if (inputStream != null) {
                                    try {
                                        inputStream.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }).start();


                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            //(1.1)
            if (accept != null) {
                try {
                    accept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //(1.2)
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

index.html

<!DOCTYPE html>
<html>
<head>
	<title></title>
	<meta charset="utf-8">
	
</head>
<body>
	<h3>访问成功</h3>
</body>
</html>
  1. 运行服务端.
  2. 打开浏览器输入 你的网页的地址,我的为

127.0.0.1:8888/src/Java学习/网络编程/TCP_UDP/TCP通信代码实现/练习4浏览器客户端/index.html
理论因该可以实现,服务端接受到浏览器的访问,然后回馈index.html 页面的,

问题:待解决,

  • 二、问题:
    1. 浏览器需要刷新2次服务端才能得到信息.访问成功????,
  • 首次输入连接不行,关闭连接或者刷新才能得到提交的信息,
  • 删除while read,一次接受文件,会一次打印3次,提交的信息????
  • 删除持续服务器的while true 无法打印连接后直接退出???
原创文章 132 获赞 11 访问量 4704

猜你喜欢

转载自blog.csdn.net/jarvan5/article/details/106020509