Java---網絡編程(3)-TCP-互傳文件和圖片

☆ TCP

創建鏈接,造成傳輸數據的通道。
在鏈接中進行大數據量傳輸
經過三次握手完成鏈接,是可靠協議
必須創建鏈接,效率會稍低java

Socket 和
ServerSocket類編程

TCP傳輸

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

Socket和ServerSocket
創建客戶端和服務器端
創建鏈接後,經過Socket中的IO流進行數據的傳輸
關閉socket 服務器

一樣,客戶端與服務器端是兩個獨立的應用程序。markdown

TCP傳輸編程

☆基本思路(客戶端)網絡

客戶端須要明確服務器的ip地址以及端口,這樣才能夠去試着創建鏈接,若是鏈接失敗,會出現異常。
鏈接成功,說明客戶端與服務端創建了通道,那麼經過IO流就能夠進行數據的傳輸,而Socket對象已經提供了輸入流和輸出流對象,經過getInputStream(), getOutputStream()獲取便可。
與服務端通信結束後,關閉Socket。併發

☆基本思路(服務器端)socket

服務端須要明確它要處理的數據是從哪一個端口進入的。
當有客戶端訪問時,要明確是哪一個客戶端,可經過accept()獲取已鏈接的客戶端對象,並經過該對象與客戶端經過IO流進行數據傳輸。
當該客戶端訪問結束,關閉該客戶端。tcp

基於TCP的Socket通訊流程

TCP傳輸編程代碼:

☆客戶端ide

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



    }

好了,基本上就是這樣的,若是想要更好,作個圖形界面就好看了。本身動下手吧~~

相關文章
相關標籤/搜索