第五十七讲 TCP编程——上传文本和图片

2019-04-15 17:29发布

本讲讲解TCP编程的两个案例,即如何上传文本或图片至服务器。

上传文本

首先编写上传文本的客户端程序,需要读取本地文本数据,发送给服务端,服务端接收完毕后,回馈"上传成功"字样。 package cn.liayun.net.tcp.uploadtext; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class UploadTextClient { public static void main(String[] args) throws UnknownHostException, IOException { /* * 上传文本的客户端,读取本地文本数据,发送给服务端,服务端接收完毕后,回馈"上传成功"字样。 */ System.out.println("上传文本客户端启动了......"); //客户端Socket Socket s = new Socket("192.168.0.102", 10005); //1,确定数据源,本地文本文件。 BufferedReader bufr = new BufferedReader(new FileReader("client.txt")); //2,确定目的,Socket输出流。 // BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); PrintWriter out = new PrintWriter(s.getOutputStream(), true); String line = null; while ((line = bufr.readLine()) != null) { out.println(line); } //3,通过Socket读取流获取服务器端返回的数据。 BufferedReader bufrIn = new BufferedReader(new InputStreamReader(s.getInputStream())); String lineIn = bufrIn.readLine();//阻塞式方法 System.out.println(lineIn); //4,关闭 bufr.close(); s.close(); } } 然后编写上传文本的服务器端程序,接收文本数据,并存储到文件中,服务端接收完毕后,回馈"上传成功"字样。 package cn.liayun.net.tcp.uploadtext; import java.io.BufferedReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class UploadTextServer { public static void main(String[] args) throws IOException { /* * 上传文本的服务器端,接收文本数据,并存储到文件中,服务端接收完毕后,回馈"上传成功"字样。 */ System.out.println("上传文本服务器端启动了......"); //1,服务器端对象 ServerSocket ss = new ServerSocket(10005); //2,获取客户端 Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + "......connected"); //3,获取读取流,确定源,网络Socket BufferedReader bufrIn = new BufferedReader(new InputStreamReader(s.getInputStream())); //4,确定目的,文件。 PrintWriter pw = new PrintWriter(new FileWriter("server.txt"), true); //5,频繁读写。 String line = null; while ((line = bufrIn.readLine()) != null) { pw.println(line); } //6,给客户端返回信息 PrintWriter out = new PrintWriter(s.getOutputStream(), true); out.println("上传成功!"); pw.close(); s.close(); ss.close(); } } 最后发现运行结果如下:
在这里插入图片描述
发现客户端与服务器端都处于阻塞状态,客户端连接上服务端,两端都在等待,没有任何数据传输。原因是read方法或者readLine方法是阻塞式方法。解决办法有两种:自定义结束标记或者使用Socket的禁用输出流方法,下面分别介绍它们。

自定义结束标记

在这里插入图片描述

使用Socket的禁用输出流方法

在这里插入图片描述

上传图片

首先编写上传图片的客户端程序,需要读取本地一张图片(例如1.jpg),发送给服务端,服务端接收完毕后,回馈"图片上传成功"字样。 package cn.liayun.net.tcp.uploadpic; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class UploadPicClient { public static void main(String[] args) throws UnknownHostException, IOException { System.out.println("上传图片客户端开启......"); //1,创建Socket客户端 Socket s = new Socket("192.168.0.102", 10006); File picFile = new File("1.jpg"); FileInputStream fis = new FileInputStream(picFile); OutputStream out = s.getOutputStream(); byte[] buf = new byte[1024]; int len = 0; while ((len = fis.read(buf)) != -1) { out.write(buf, 0, len); } //告诉服务器端写完了 s.shutdownOutput(); //读取服务器端的数据 InputStream in = s.getInputStream(); byte[] bufIn = new byte[1024]; int lenIn = in.read(bufIn); String str = new String(bufIn, 0, lenIn); System.out.println(str); fis.close(); s.close(); } } 然后编写上传图片的服务器端程序,接收图片数据,并存储到指定位置的文件中,服务端接收完毕后,回馈"图片上传成功"字样。 package cn.liayun.net.tcp.uploadpic; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class UploadPicServer { public static void main(String[] args) throws IOException { System.out.println("上传图片服务器端开启......"); //服务器端对象 ServerSocket ss = new ServerSocket(10006); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + "......connected"); File file = getFile("d:\server_pic", ip); InputStream in = s.getInputStream(); FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[1024]; int len = 0; while ((len = in.read(buf)) != -1) { fos.write(buf, 0, len); } //回馈客户端数据 OutputStream out = s.getOutputStream(); out.write("图片上传成功".getBytes()); fos.close(); s.close(); ss.close(); } private static File getFile(String dir, String ip) { File pic_dir = new File(dir); if (!pic_dir.exists()) { pic_dir.mkdirs(); } int count = 1; File file = new File(pic_dir, ip + "(" + count + ").jpg"); while (file.exists()) { count++; file = new File(pic_dir, ip + "(" + count + ").jpg"); } return file; } } 最后,运行结果如下:
在这里插入图片描述
多次上传同一张图片,为了防止在服务器中存储的图片名称重名,我们可以依次顺延,如下图所示:
在这里插入图片描述

并发访问问题

现实场景中,不只有你一个人上传图片到服务器中,真实情况是有多人同时上传图片,这种情况咋解决?即服务器内部如何实现并发访问。
要解决该问题肯定是要用到多线程技术的。首先编写一个线程任务类——UploadPic.java,将来访的客户端都封装到一个单独的线程中。 package cn.liayun.net.tcp.uploadpic; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class UploadPic implements Runnable { private Socket s; public UploadPic(Socket s) { super(); this.s = s; } @Override public void run() { String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + "......connected"); File file = getFile("d:\server_pic", ip); InputStream in; try { in = s.getInputStream(); FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[1024]; int len = 0; while ((len = in.read(buf)) != -1) { fos.write(buf, 0, len); } //回馈客户端数据 OutputStream out = s.getOutputStream(); out.write("上传图片成功".getBytes()); fos.close(); s.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static File getFile(String dir, String ip) { File pic_dir = new File(dir); if (!pic_dir.exists()) { pic_dir.mkdirs(); } int count = 1; File file = new File(pic_dir, ip + "(" + count + ").jpg"); while (file.exists()) { count++; file = new File(pic_dir, ip + "(" + count + ").jpg"); } return file; } } 这时,服务器端的程序应该修改为: package cn.liayun.net.tcp.uploadpic; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class UploadPicServer { public static void main(String[] args) throws IOException { System.out.println("上传图片服务器端开启了......"); ServerSocket ss = new ServerSocket(10006); while (true) { //服务器端对象 //ServerSocket ss = new ServerSocket(10006);//异常:java.net.BindException: Address already in use: JVM_Bind //两个服务端用的是一个端口,端口被占用。 Socket s = ss.accept(); new Thread(new UploadPic(s)).start(); } // ss.close(); } }