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

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

  1. ACE中TCP通信

    转载于:http://www.cnblogs.com/TianFang/archive/2006/12/07/585095.html 概述: 传输控制协议TCP(Transmission Contro ...

  2. 文档通信(跨域-不跨域)、时时通信(websocket)、离线存储(applicationCache)、开启多线程(web worker)

    一.文档间的通信 postMessage对象 //不跨域 1.iframe:obj.contentWindow [iframe中的window对象] iframe拿到父级页面的window: pare ...

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

    多线程基础 . TCP通信 * 当一个方法被synchronized修饰后,那么 * 该方法称为同步方法,即:多个线程不能同时 * 进入到方法内部执行. package day10; /** * 当多 ...

  4. UE4 Sockets多线程TCP通信

    转自:https://blog.csdn.net/zilisen/article/details/75007447 一.简介 UE4引擎是提供了Sockets模块和Networking模块的,博主在研 ...

  5. TCP通信---文件上传案例、多线程文件上传

    目前大多数服务器都会提供文件上传的功能,由于文件上传需要数据的安全性和完整性,很明显需要使用TCP协议来实现. TCP通信需要创建一个服务器端程序和一个客户端程序,实现客户端向服务器端上传文件 代码实 ...

  6. 客户端程序通过TCP通信传送"小文件"到服务器

    客户端程序通过TCP通信传送"小文件"到服务器 [c#源码分享]客户端程序通过TCP通信传送"小文件"到服务器 源码  (不包含通信框架源码,通信框架源码请另行 ...

  7. 第4章 TCP/IP通信案例:访问Internet上的Web服务器

    第4章 TCP/IP通信案例:访问Internet上的Web服务器 4.2 部署代理服务器 书中为了演示访问Internet上的Web服务器的全过程,使用了squid代理服务器程序模拟了一个代理服务器 ...

  8. TCP通信的客户端代码实现和TCP通信的服务器代码实现

    TCP通信的客户端代码实现 package com.yang.Test.ServerStudy; import java.io.*; import java.net.Socket; /** * TCP ...

  9. Java 之 TCP 通信程序

    一.概述 TCP 通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server). 两端通信时步骤: 1.服务端程序,需要事先启动,等待客户端的连接: 2. ...

随机推荐

  1. C++ STL(标准模板库)

    一.STL简介 STL(Standard Template Library,标准模板库)是惠普实验室开发的,在被引入C++之前该技术就已经存在了很长的一段时间. STL的代码从广义上讲分为三类:alg ...

  2. Linux之vim按键

    1. 移动光标的方法 h或左箭头 光标向左移动一个字符 j或下箭头 光标向下移动一个字符 k或上箭头 光标向上移动一个字符 l或右箭头 光标向右移动一个字符 如果想要向下移动30行,可以使用“30j” ...

  3. Codeforces 矩阵题 题单

    Matrix CF 166E Tetrahedron dp方程设为 f[i] 最后在 D点,g[i] 表示最后不在D点.最后 g[] 可以通过矩阵加速数列求得,数据可以强化,复杂度 \(O(logn) ...

  4. 2018微信小程序开发遇到的坑

    第一个坑:wx.showModal(OBJECT) wx.showModal在安卓手机里,如果点击遮罩的话会关闭弹窗,不会有任何回调.而苹果的情况下则是点击遮罩不会有任何反应. 这样会有什么问题呢? ...

  5. java: Set类及子类:TreeSet有序子类,HashSet无序子类:重复元素

    Set类及子类: TreeSet有序子类: HashSet无序(散列)子类 HashSet子类的内容是没有顺序的,单个元素也不会重复的(对象除外). Set<String> allSet ...

  6. Python---面向对象---龟鱼游戏

    一.定义一个门票系统 门票的原价是100元 当周末的时候门票涨价20% 小孩子半票 计算2个成人和1个小孩的平日票价 ----------------------------------------- ...

  7. linux运维、架构之路-MySQL(一)

    一.数据库管理系统 1.RDBMS关系型数据库特点 ①二维表 ②典型产品Oracle传统企业,MySQL是互联网企业产品 ③数据存取通过SQL ④最大的特点,数据安全性很强(ACID) 2.NoSQL ...

  8. Vue框架基础概要

    Vue.js是什么? Vue.js(读音 /vjuː/,类似于 view 的读音)是一套构建用户界面(user interface)的渐进式框架.与其他重量级框架不同的是,Vue 从根本上采用最小成本 ...

  9. [luogu]P2279 [HNOI2003]消防局的设立[贪心]

    [luogu]P2279 [HNOI2003]消防局的设立 题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两 ...

  10. [luogu]P1053 篝火晚会[数学][群论]

    [luogu]P1053 篝火晚会 题目描述 佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”.在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会.一共有n个同 ...