TCP通信 - 服务器开启多线程与read()导致服务器阻塞问题

TCP通信的文件上传案例


  • 本地流:客户端和服务器和本地硬盘进行读写,需要使用自己创建的字节流
  • 网络流:客户端和服务器之间读写,必须使用Socket中提供的字节流对象

客户端工作:读取本地文件,上传到服务器,读取服务器回写的数据


  • 明确数据源
  • 目的地:服务器

  • 客户端代码:
package cn.learn.web;

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

public class client {
    public static void main(String[] args) throws IOException {
        //创建一个本地的字节输入流,构造方法绑定数据源
        FileInputStream fileIn = new FileInputStream("b.txt");

        //*****发送数据******
        //创建客户端对象Socket,构造方法绑定服务器IP地址和端口号
        Socket socket = new Socket("127.0.0.1", 8020);
        //使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream socketOut = socket.getOutputStream();

        //读入内存
        int len = 0;
        byte[] bytes = new byte[1024];
        //使用流中的write方法给服务器发送数据
        while ((len = fileIn.read(bytes)) != -1) {
            socketOut.write(bytes, 0, len);
        }

        //已上传完文件,但read()导致服务器阻塞,给服务器写一个结束标记
        socket.shutdownOutput();

        //使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream clientIn = socket.getInputStream();
        //使用InputStream对象中的read()方法,读取服务器回写的数据
        while ((len = clientIn.read(bytes)) != -1) {
            //打印看看
            System.out.println(new String(bytes, 0, len));
        }

        //释放资源,只关闭Socket的IO流就行
        fileIn.close();
        socket.close();

    }
}

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


  • 明确:

    1. 数据源:客户端上传的文件
    2. 目的地:服务器的硬盘 d:\\upload.txt

  • 服务器代码:
package cn.learn.web;

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

public class Server {
    public static void main(String[] args) throws IOException {

        //设置通信端口号,不然系统随机分配
        ServerSocket server = new ServerSocket(8020);

        //使用serverSocket对象中的方法accept,获取到请求的客户端对象Socket(含地址和端口号)
        Socket socket1 = server.accept();

        //使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream serveIn = socket1.getInputStream();

        //判断文件夹是否存在,若不存在新建
        File file = new File("d:\\upload");
        if(!file.exists()){
            file.mkdir();
        }

        //创建服务器的本地字节输出流,构造方法绑定输出位置,注意****\\******
        FileOutputStream localOut = new FileOutputStream(file+"\\b.txt");

        //获取读取的数据有效长度,循环读取和写入服务器硬盘
        byte[] bytes = new byte[1024];
        int len = 0;
        //使用serveIn的方法read,读取客户端发送的数据
        while ((len = serveIn.read(bytes)) != -1) {
            localOut.write(bytes,0,len);
            //打印到控制台看看
            System.out.println(new String(bytes, 0, len));

        }

        //使用Socket对象中的方法getOutputStream()获取网络字节输入流OutputStream对象
        OutputStream serverOut = socket1.getOutputStream();

        //6.使用serverOut中的write方法回写给客户端
        serverOut.write("我收到了,上传成功".getBytes());

        //7.释放socket1与server的流
        server.close();
        socket1.close();
    }
}

注:服务器一直开启,等待客户端上传


  • TCP通信完成后服务器一直未停止原因

    1. read()方法,若没有输入可用,方法阻塞
    2. 具体原因是read()读取时用的循环,读不到结束标记,服务器一直等待进入阻塞状态

//已上传完文件,但read()导致阻塞,给服务器写一个结束标记
socket.shutdownOutput();

上述代码优化


  • 优化服务器存储文件名称

        /*
        自定义服务器本地存储文件名称,防止冲突覆盖
        规则:域名+毫秒值+随机数
         */
        String filename = "learn" + System.currentTimeMillis()+ (new Random().nextInt(10)+1)+".txt";

        //创建服务器的本地字节输出流,构造方法绑定输出位置,注意****\\******
        FileOutputStream localOut = new FileOutputStream(file+"\\"+filename);
  • 使服务器一直处于监听状态,循环accept接受的套接字操作(而不是阻塞),最后不用关闭服务器了
  • 在循环中使用多线程,提高效率,有一个客户端上传文件,就开启一个线程。将代码块复制到run()方法中,注意使用try catch抛出异常

服务器代码优化,开启多线程

package cn.learn.web;

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

public class Server {
    public static void main(String[] args) throws IOException {

        //设置通信端口号,不然系统随机分配
        ServerSocket server = new ServerSocket(8020);

        /*
        循环,使得服务器一直处于监听状态,有客户端上传文件,就存入
         */
        while (true) {

            //使用serverSocket对象中的方法accept,获取到请求的客户端对象Socket(含地址和端口号)
            Socket socket1 = server.accept();

            /******
             客户端每上传一个文件,就开启一个多线程
             ******/
            new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        //使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
                        InputStream serveIn = socket1.getInputStream();

                        //判断文件夹是否存在,若不存在新建
                        File file = new File("d:\\upload");
                        if (!file.exists()) {
                            file.mkdir();
                        }

                        /*
                        自定义服务器本地存储文件名称,防止冲突覆盖
                        规则:域名+毫秒值+随机数
                        */
                        String filename = "learn" + System.currentTimeMillis() + (new Random().nextInt(10) + 1) + ".txt";

                        //创建服务器的本地字节输出流,构造方法绑定输出位置,注意****\\******
                        FileOutputStream localOut = new FileOutputStream(file + "\\" + filename);

                        //获取读取的数据有效长度,循环读取和写入服务器硬盘
                        byte[] bytes = new byte[1024];
                        int len = 0;
                        //使用serveIn的方法read,读取客户端发送的数据
                        while ((len = serveIn.read(bytes)) != -1) {
                            localOut.write(bytes, 0, len);
                        }

                        //使用Socket对象中的方法getOutputStream()获取网络字节输入流OutputStream对象
                        OutputStream serverOut = socket1.getOutputStream();

                        //6.使用serverOut中的write方法回写给客户端
                        serverOut.write("我收到了,上传成功".getBytes());

                        //7.释放socket1与server的流
                        socket1.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

原文地址:https://www.cnblogs.com/huxiaobai/p/11614101.html

时间: 2024-08-27 23:31:23

TCP通信 - 服务器开启多线程与read()导致服务器阻塞问题的相关文章

JAVASE02-Unit010: 多线程基础 、 TCP通信

多线程基础 . TCP通信 * 当一个方法被synchronized修饰后,那么 * 该方法称为同步方法,即:多个线程不能同时 * 进入到方法内部执行. package day10; /** * 当多线程并发操作同一资源时,由于线程切换的不确定 * 性,可能导致执行顺序的混乱,严重时可能导致系统 * 瘫痪. * @author adminitartor * */ public class SyncDemo1 { public static void main(String[] args) { f

【c#源码】安卓客户端通过TCP通信与Windows服务器进行文件传输

APK文件  (对应的windows服务器端已经架设好,可以直接下载进行测试) 源码     数据库文件 在前面一篇文章:[源码]c#编写的安卓客户端与Windows服务器程序进行网络通信 中我们探讨了,如何通过xamarin技术,完成安卓客户端与Windows服务器的通信,这篇文章,我们探讨一下使用场景非常多的文件传输. 先谈一下为什么使用xamarin.android技术吧,之前有开发过一个公文系统,c#语言开发,服务器部署在Windows Server 2003上,客户端采用Winform

客户端通过TCP通信分页从服务器获取数据

本文主要探讨,客户端如何通过TCP通信,根据分页信息从服务器获取到相关数据 通常情况下,数据可能很多,比如几千或者几万条,并不适合一次性从服务器获取. 我们只从服务器获取当前页的数据 和数据库中记录总数以便我们可以在客户端计算出页数 ,当用户点击"上一页"或者"下一页"时,再获取相应页的数据 如下图: 双击上图中的某一项,再打开详细页面,如下图: 下面我们从头开始创建一个分页显示的Demo 通信框架采用来自英国开源的networkcomms2.3.1版本 数据库为s

[c#源码分享]客户端程序通过TCP通信传送"小文件"到服务器

源码  (不包含通信框架源码,通信框架源码请另行下载) 上一篇文章写了如何通过TCP通信发送图片到客户端,有朋友问如何传送文件,本文将就如何发送文件进行探讨. 对于比较小的文件,可以把文件转化成字节形式,用契约类包装一下,服务器收到后,再把字节转化成文件即可,这也是本文中实现的方式,这种方式的优点是比较简单灵活,缺点是不适合大文件的发送,也不能显示文件发送的进度. 基于TCP的通信机制,对于比较大的文件,这种方式是不可行的,大文件采用分段发送再合成的方式比较好,以后有时间再对如何发送大文件单独探

Linux网络编程——tcp并发服务器(多线程)

tcp多线程并发服务器 多线程服务器是对多进程服务器的改进,由于多进程服务器在创建进程时要消耗较大的系统资源,所以用线程来取代进程,这样服务处理程序可以较快的创建.据统计,创建线程与创建进程要快 10100 倍,所以又把线程称为"轻量级"进程.线程与进程不同的是:一个进程内的所有线程共享相同的全局内存.全局变量等信息,这种机制又带来了同步问题. tcp多线程并发服务器框架: 我们在使用多线程并发服务器时,直接使用以上框架,我们仅仅修改client_fun()里面的内容. 代码示例: #

你的MySQL服务器开启SSL了吗?SSL在https和MySQL中的原理思考

最近,准备升级一组MySQL到5.7版本,在安装完MySQL5.7后,在其data目录下发现多了很多.pem类型的文件,然后通过查阅相关资料,才知这些文件是MySQL5.7使用SSL加密连接的.本篇主要介绍MySQL5.7 SSL连接加密功能.如何使用?以及使用SSL的一些注意点. 我们知道,MySQL5.7之前版本,安全性做的并不够好,比如安装时生成的root空密码账号.存在任何用户都能连接上的test库等,导致数据库存在较大的安全隐患.好在5.7版本对以上问题进行了一一修复.与此同时,MyS

进程通信概念简介 多线程上篇(六)

进程通信指的是进程间的信息交换 ,IPC(Inter-Process Communication,进程间通信) 之前说到: 进程通信就相当于一种工作方式.沟通形式,比如你给我一个SVN标签号并且告知我意图,我去库中检索指定标签修改的指定内容,就完成了一个任务的协作. 进程同步中,也有信息的交换,比如互斥量访问,再比如生产者和消费者,共享缓冲池,但是这些通常被称之为低级的进程通信. 以信号量为例,如果你说我在塔顶点亮灯表示危险,否则表示安全,这没问题,如果你想用灯亮灯灭来像QQ一样,大段大段的传递

TCP通信详解

一.TCP简介 1.TCP介绍 a>TCP协议:TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为:TCP)是一种面向连接的.可靠的.基于字节流的通信协议 1.面向连接:先连接,再通信,好比打电话模型 2.可靠的,相对于UDP,TCP传输更可靠,TCP通过一序列的机制(面向连接机制.发送应答机制)来保障传输的可靠性 3.基于字节流的,UDP创建UDP socket--DGRAM:基于数据报通信方式,每一次发送的数据都是一个独立的整体,包含目标主机的

C# 实现TCP通信

1.TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用.http协议在应用层运行. 02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而TCP提供传输保证. 03,网络层(Netwok):网络层协议由一系列协议组成,包括ICMP.IGMP.RIP.OSPF.IP(v4,v6)等. 04,链路层(Link):又称为物