Java---网络编程(3)-TCP-互传文件和图片

TCP

建立连接,形成传输数据的通道。
在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低

Socket 和
ServerSocket类

TCP传输

TCP Socket:IP地址和端口,套接字

Socket和ServerSocket
建立客户端和服务器端
建立连接后,通过Socket中的IO流进行数据的传输
关闭socket

同样,客户端与服务器端是两个独立的应用程序。

TCP传输编程

基本思路(客户端)

客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(), getOutputStream()获取即可。
与服务端通讯结束后,关闭Socket。

基本思路(服务器端)

服务端需要明确它要处理的数据是从哪个端口进入的。
当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。
当该客户端访问结束,关闭该客户端。

基于TCP的Socket通信流程

TCP传输编程代码:

客户端

通过Socket建立对象并指定要连接的服务端主机以及端口。

Socket s = new Socket(“192.168.1.1”,9999);
OutputStream out = s.getOutputStream();
out.write(“hello”.getBytes());
s.close();

服务器端

建立服务端需要监听一个端口

ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();

最简单的TCP演示实例:

客户端代码:

package cn.hncu.tcp;

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

/**
 * 客户端
 * @author 陈浩翔
 *
 * 2016-5-10
 */
public class MyClientSocket {

    public static void main(String[] args) {
        try {
            //因为是在自己本机上演示,IP就直接填写本机10.30.7.95的了。
            //这个端口和IP都是服务器端的(自己可以改的)
            Socket s = new Socket("10.30.7.95", 9999);
            //和服务器进行三次握手,若失败则出异常,否则返回和对方通讯的socket

            OutputStream os = s.getOutputStream();
            //发送数据
            os.write("你好,服务器!".getBytes());

            //接收服务器端的反馈
            InputStream in = s.getInputStream();

            DataInputStream din = new DataInputStream(in);

            System.out.println(din.readUTF());

            s.close();
            din.close();

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

    }

}

服务器端代码:

package cn.hncu.tcp;

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

/**
 * 服务器端
 * @author 陈浩翔
 *
 * 2016-5-10
 */
public class MyServerSocket {

    public static void main(String[] args) {

        try {
            ServerSocket server = new ServerSocket(9999);
            while(true){

                System.out.println("准备接收一个数据...");
                Socket s = server.accept();//阻塞式方法
                System.out.println("接收了一个数据...");

                //读--从客户端读数据
                InputStream in =  s.getInputStream();
                byte buf[] = new byte[1024];
                in.read(buf);
                System.out.println("read info: "+new String(buf));

                //写--应答客户端--向他写数据
                OutputStream out = s.getOutputStream();
                DataOutputStream dout = new DataOutputStream(out);
                dout.writeUTF("你好,"+s.getInetAddress().getHostAddress()+"  ,你的信息已收到。");
                dout.close();
                s.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

客户端运行结果:

你好,10.30.7.95  ,你的信息已收到。

服务器端运行结果:

思考

  对于Web服务器而言,当有多个客户端同时访问服务器时,服务端又如何提供服务呢?

TCP传输最容易出现的问题

客户端连接上服务端,两端都在等待,没有任何数据传输。
通过例程分析:
因为read方法或者readLine方法是阻塞式。
解决办法:
自定义结束标记(必须定义传输文件中没有这个这个字符串的,不然会出现接收数据不完整)
使用shutdownInput,shutdownOutput方法。

编程练习

上传文本文件

读取一个本地文本文件,将数据发送到服务端,服务器端对数据进行存储。 存储完毕后,给客户端一个提示。

文本文件发送的客户端

package cn.hncu.tcp.upload;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 文本文件发送的客户端
 * @author 陈浩翔
 *
 * 2016-5-10
 */
public class UploadTextClient {

    public static void main(String[] args) {

        try {
            Socket s = new Socket("", 10006);

            //思路:把本地文件的数据读取出来通过 s.getOutputStream()获得的out对象发送出去
            BufferedReader bf = new BufferedReader(new FileReader("tempfiles\\client.txt"));

            OutputStream out = s.getOutputStream();//这里的输出流 对应的是服务器端的输入流
            PrintWriter pw = new PrintWriter(out,true);//建议不要用BufferedWriter
            //!!!!!!!!!!!!!!!!!这个true不要忘了!---自动刷新
            //现在大家写网络传输文件,一般是用PrintWriter

            String str=null;
            while((str=bf.readLine())!=null){
                pw.println(str);
            }

            //给服务器发送结束标记---上传结束,要加结束标记,
            //否则服务器在数据接收完毕时再调用read()或readLine()时会出异常
            //法1:pw.println("over#$@#@$");//不能出现文件中存在的结束关键字---搞特殊一点
            //法2---建议采用该种方式---由socket内部来指定结束标记
            s.shutdownOutput();
            bf.close();

            //接收服务器端反馈

            InputStream in = s.getInputStream();
            DataInputStream din = new DataInputStream(in);
            System.out.println("server应答:"+din.readUTF());
            s.close();
            din.close();

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

    }

}

文本文件接收的服务器端

package cn.hncu.tcp.upload;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 文本文件接收的服务器端
 * @author 陈浩翔
 *
 * 2016-5-10
 */
public class UploadTextServer {

    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(10006);

            Socket s = server.accept();
            System.out.println(s.getInetAddress().getHostAddress()+"...发送消息来");

            //读取客户端上传过来的文本文件
            //源 ---socket(字节流)---额外:需要转换成字符流  ,缓存流
            InputStream in  = s.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            //目的 ---硬盘字符流 FileWriter---额外:打印流
            PrintWriter pw = new PrintWriter(new FileWriter("tempfiles\\server.txt"),true);
            String line = null;
            while((line=br.readLine())!=null){
//              if("over#$@#@$".equals(line)){//自己定义的结束标志
//              break;
//          }
                pw.println(line);
            }

            pw.close();

            //上传成功,给客户端一个提示信息

            OutputStream out = s.getOutputStream();
            DataOutputStream dout = new DataOutputStream(out);
            dout.writeUTF("文件上传成功!");
            s.close();
            server.close();
            dout.close();

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

    }

}

测试通过!

上传图片文件

客户端需求:把一个图片文件发送到服务端并读取回馈信息。要求判断文件是否存在及格式是否为jpg或gif并要求文件小于2M。
服务端需求:接收客户端发送过来的图片数据。进行存储后,回馈一个 上传成功字样。支持多用户的并发访问。

图片文件的发送-客户端

package cn.hncu.tcp.upload;

import java.io.BufferedInputStream;
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;
import java.util.Scanner;

import javax.swing.JOptionPane;

/**
 * 图片文件的发送-客户端
 * @author 陈浩翔
 *
 * 2016-5-10
 */
public class UploadPicClient {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入图片路径:");
        String str = sc.next();

        File file = new File(str);
        if(!(file.exists()&& file.isFile())){
            JOptionPane.showMessageDialog(null, "文件不存在!");
            return ;
        }

        if(!(file.getName().endsWith(".jpg")||file.getName().endsWith(".gif"))){
            JOptionPane.showMessageDialog(null, "文件格式不对,文件扩展名必须是jpg或gif!");
            return ;
        }
        if( file.length()>=1024*1024*2){
            JOptionPane.showMessageDialog(null, "文件过大,不应超过2M,请重新上传!");
            return;
        }

        //上传
        try {
            Socket s = new Socket("10.30.7.95", 10007);

            BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str));

            OutputStream out = s.getOutputStream();

            byte buf[] = new byte[1024];
            int len=0;
            while((len=bin.read(buf))!=-1){
                out.write(buf, 0, len);
            }

            s.shutdownOutput();//告诉服务器,文件上传完毕

            //读取服务器的回馈信息
            InputStream in = s.getInputStream();
            byte buf2[] = new byte[1024];
            int len2 = in.read(buf2);
            System.out.println(new String(buf2, 0, len2));

            //关流
            out.close();
            bin.close();
            s.close();

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

    }

}

图片文件接收-服务器端

package cn.hncu.tcp.upload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 图片文件接收-服务器端
 * @author 陈浩翔
 *
 * 2016-5-10
 */
public class UploadPicServer {

    public static void main(String[] args) {

        try {
            ServerSocket server = new ServerSocket(10007);
            while(true){
                Socket s = server.accept();//阻塞方法
                //只负责和客户端进行握手
                new Thread(new  UploadThread(s) ).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class UploadThread implements Runnable{
    private Socket s;
    public UploadThread(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {

        String ip = s.getInetAddress().getHostAddress();
        System.out.println(ip+"...发来图片");

        try {
            BufferedInputStream bin = new BufferedInputStream(s.getInputStream());

            File dir = new File("g:\\mypic");

            if(!dir.exists()){
                dir.mkdir();//文件夹不存在,创建mypic文件夹
            }

            int count=1;
            //我觉得这里的后缀名,需要通过发送方也发过来的
            File file = new File(dir, ip+".jpg");

            while(file.exists()){
                file = new File(dir,ip+"("+(count++) +")"+".jpg"); //带号的文件名
            }

            FileOutputStream fout = new FileOutputStream(file);

            //从socket流中读取数据,存储到本地文件。相当于对拷
            byte buf[] = new byte[1024];
            int len=0;
            while( (len=bin.read(buf))!=-1){
                fout.write(buf, 0, len);
            }
            //图片接收完毕

            //向客户端发送回馈信息
            OutputStream out = s.getOutputStream();
            out.write( "上传成功".getBytes() );

            fout.close();
            s.close();

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

    }
}

有一个小bug。是服务端接收的,因为我把后缀名统一为jpg了,gif的图片可以上传,只是变成静图了(jpg)。可以通过改后缀名,再把这图片改回去动图。还有一种方法,在上传的时候,把后缀名也上传,再通过服务器解析就可以解决这个问题。
因为时间问题,我就不写了,这个很简单的。
只是多发送了一个后缀名过去而已。

我还是把那个后缀名的解决办法写了下:

在客户端:修改的代码如下:

//上传
        try {
            Socket s = new Socket("10.30.7.95", 10007);

            BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str));

            //上传文件后缀###########增加的
            OutputStream out = s.getOutputStream();
            String fileName = file.getName();
            out.write(fileName.substring(fileName.length()-4, fileName.length()).getBytes());

            byte buf[] = new byte[1024];
            int len=0;
            while((len=bin.read(buf))!=-1){
                out.write(buf, 0, len);
            }

            s.shutdownOutput();//告诉服务器,文件上传完毕

            //读取服务器的回馈信息
            InputStream in = s.getInputStream();
            byte buf2[] = new byte[1024];
            int len2 = in.read(buf2);
            System.out.println(new String(buf2, 0, len2));

            //关流
            out.close();
            bin.close();
            s.close();

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

服务器端:

@Override
    public void run() {

        String ip = s.getInetAddress().getHostAddress();
        System.out.println(ip+"...发来图片");

        try {

            BufferedInputStream bin = new BufferedInputStream(s.getInputStream());

            File dir = new File("g:\\mypic");

            if(!dir.exists()){
                dir.mkdir();//文件夹不存在,创建mypic文件夹
            }

            //读取上传过来的图片后缀是什么!#########
            char cbuf[] = new char[4];
            InputStreamReader insr = new InputStreamReader(bin);
            insr.read(cbuf);
            String str = new String(cbuf);

            int count=1;
            //我觉得这里的后缀名,需要通过发送方也发过来的
            File file = new File(dir, ip+str);

            while(file.exists()){
                file = new File(dir,ip+"("+(count++) +")"+str); //带号的文件名
            }

            FileOutputStream fout = new FileOutputStream(file);

            //从socket流中读取数据,存储到本地文件。相当于对拷
            byte buf[] = new byte[1024];
            int len=0;
            //#########必须有这一句
            bin.read(buf, 0, 8);
            while( (len=bin.read(buf))!=-1){
                fout.write(buf, 0, len);
            }
            //图片接收完毕

            //向客户端发送回馈信息
            OutputStream out = s.getOutputStream();
            out.write( "上传成功".getBytes() );

            fout.close();
            s.close();

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

    }

好了,基本上就是这样的,如果想要更好,做个图形界面就好看了。自己动下手吧~~

时间: 2024-09-22 03:58:53

Java---网络编程(3)-TCP-互传文件和图片的相关文章

java 网络编程 UDP TCP

网络编程 网络编程主要用于解决计算机与计算机(手机.平板..)之间的数据传输问题. 网络编程: 不需要基于html页面就可以达到数据之间的传输. 比如: feiQ , QQ , 微信.... 网页编程: 就是要基于html页面的基础上进行数据的交互的. 比如: 珍爱网. oa(办公自动化). 高考的报告系统... 计算机网络: 分布在不同地域 的计算机通过外部设备链接起来达到了消息互通.资源共享的效果就称作为一个计算机网络. 网络通讯的三要素:     1. IP     2. 端口号.   

java-Java 网络编程 聊天软件和发送文件

问题描述 Java 网络编程 聊天软件和发送文件 package ch09; import java.awt.*;import java.awt.event.*;import java.io.*;import java.net.*; import javax.swing.*; public class Server extends JFrame implements ActionListenerRunnable{ JTextArea showArea = new JTextArea(); //聊

学习了java网络编程,看到tcp ip那里时,书上讲tcp的内容时,只讲了tcp服务器端

问题描述 学习了java网络编程,看到tcp ip那里时,书上讲tcp的内容时,只讲了tcp服务器端 没有讲客户端,只是说用电脑的telnet去链接,那么java一般怎么用自己编写的程序去链接tcp服务器呢,这个该怎么写,有关于这方面的书籍吗,或是这方面的知识该去哪里去找? 解决方案 http://blog.csdn.net/wintys/article/details/3525619 解决方案二: java网络编程,java核心技术卷二 解决方案三: java核心技术卷二.. 解决方案四: j

Java网络编程之简单的服务端客户端应用实例_java

本文实例讲述了Java网络编程之简单的服务端客户端应用.分享给大家供大家参考.具体如下: 在Java中,我们使用java.net.Socket及其相关类来完成有关网络的相关功能.Socket类非常简单易用,因为Java技术隐藏了建立网络连接和通过连接发送数据的复杂过程.下面所说的内容只适用于TCP协议. 一.连接到服务器 我们可以使用Socket类的构造函数来打开一个套接字,如 Socket sk = new Socket("210.0.235.14",13); 其中,210.0.23

linux 之间互传文件

两个linux 之间如何互传文件呢? 安装QQ? 但是有的linux 系统安装不了QQ. 使用U盘,太麻烦了! 使用samba?还得一堆配置  方法一:scp 使用scp 命令就可以轻松搞定. Java代码    scp root@182.92.191.38:/usr/local/src/freetds-stable.tgz ./      scp root@182.92.191.38:/var/www/html/sql2.php /var/www/html/       scp root@18

Java网络编程(一)

关于JAVA网络编程的技术非常繁多,如:SOCKET.RMI.EJB.WEBSERVICE.MQ.中间数据等等方法,但是万变都是源于基础中通信原理,有些是轻量级的.有重量级的:有实时调用.有异步调用:这么多的技术可以说什么都可以用,关键在什么场合用什么最适合你,这些技术主要用于多个子系统之间相互通信的方法,如一个大型的软件应用分多个子系统,它们可能由不同的厂商来完成,这些子系统最终需要整合为一个系统,那么整合的基础就是交互,要么是通过数据交互,要么是通过接口调用,要么通过中间数据等等.本文从最基

iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载

前言:iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载是在前篇iOS开发之网络编程--使用NSURLConnection实现大文件下载的基础上进行    断点续传的设置.关于iOS开发之网络编程--使用NSURLConnection实现大文件下载的细节这里当然就不会再累述的啦.   断点续传的原理:    每次在向服务器请求下载数据的同时,要告诉服务器从整个下载文件的数据流的某个还未下载的位置开始下载,然后服务器就返回从那个位置开始的数据流.    图片示意图:

java网络编程-两台电脑IP地址一样!同一个无线

问题描述 两台电脑IP地址一样!同一个无线 今天学习java网络编程 先用TCP 客户端服务端实现两台电脑通讯,成功了,当时一台是192.168.1.104,一台是105.然后我想试下udp,结果失败了,查了下ip,两台电脑都成104了,这是怎么回事啊 解决方案 IP一样怎么能通讯呢 解决方案二: 那你把两台计算机的IP地址设成不一样的再试不就好了么 解决方案三: 你改回105就可以了啊,获得你的端口号改变下 解决方案四: 估计是得修改一下IP了,可能是你那里没有写对吧

app-想写个安卓互传文件的APP

问题描述 想写个安卓互传文件的APP 在无网络的条件下传输,还有在无网络下如何传输等的问题. 类似飞鸽传书那种,大家提供点思路,和资料,源码. 谢谢大家. 解决方案 无网络得使用蓝牙来传了,参考:http://blog.sina.com.cn/s/blog_6397bdd201018mlg.html 解决方案二: 文件互传APP

Java网络编程从入门到精通

Hibernate从入门到精通(十一)多对多双向关联映射 Hibernate从入门到精通(十)多对多单向关联映射 Hibernate从入门到精通(九)一对多双向关联映射 Hibernate从入门到精通(八)一对多单向关联映射 Hibernate从入门到精通(七)多对一单向关联映射 Hibernate从入门到精通(六)一对一双向关联映射 Hibernate从入门到精通(五)一对一单向关联映射 Hibernate从入门到精通(四)基本映射 Hibernate从入门到精通(三)Hibernate配置文