雜談socket編程

作者:xcbeyond
瘋狂源自夢(mèng)想,技術(shù)成就輝煌!微信公眾號(hào):《程序猿技術(shù)大咖》號(hào)主,專注后端開發(fā)多年,擁有豐富的研發(fā)經(jīng)驗(yàn),樂于技術(shù)輸出、分享,現(xiàn)階段從事微服務(wù)架構(gòu)項(xiàng)目的研發(fā)工作,涉及架構(gòu)設(shè)計(jì)、技術(shù)選型、業(yè)務(wù)研發(fā)等工作。對(duì)于Java、微服務(wù)、數(shù)據(jù)庫、Docker有深入了解,并有大量的調(diào)優(yōu)經(jīng)驗(yàn)。

最近在寫一些與網(wǎng)絡(luò)編程相關(guān)的小工具,不得不再把網(wǎng)絡(luò)編程的知識(shí)再回顧回顧了。一提起網(wǎng)絡(luò)編程,勢(shì)必局想到了一位核心人物Socket,是她撐起了網(wǎng)絡(luò)編程的半邊天,使得網(wǎng)絡(luò)編程得以繼續(xù)。

         什么是Socket?

        通常也稱作"套接字",應(yīng)用程序通過Socket向網(wǎng)絡(luò)發(fā)出請(qǐng)求或者應(yīng)答網(wǎng)絡(luò)請(qǐng)求。它是在應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口,用來組織傳輸?shù)臄?shù)據(jù)。下面就用一張圖來說明下“三次握手”:

         

         網(wǎng)絡(luò)間的通信,是建立在授受雙方的,即:服務(wù)器與客戶端。服務(wù)器端時(shí)刻處于啟動(dòng)狀態(tài),不停地監(jiān)聽著客戶端發(fā)來的請(qǐng)求信息,并給予應(yīng)答,當(dāng)客戶端向服務(wù)器發(fā)來一條數(shù)據(jù)請(qǐng)求時(shí),服務(wù)器接受到了這條請(qǐng)求后,驗(yàn)證成功后,給出回復(fù),建立鏈接通信,即一次握手。類似數(shù)據(jù)傳輸交換、斷開鏈接又是兩次握手。下面就以一段代碼來做一分析說明吧。(Java版)

服務(wù)端:

<span style="font-size:14px;">import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
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;
/**
 * FTP服務(wù)器端
 * @author xcbeyond
 * 2014-6-7 22:12:21
 */
 
public class FtpServer {
    public static void main(String[] args) throws Exception{
        FtpServer server = new FtpServer();
        server.listen(8800);
        
    }
        //用來時(shí)刻監(jiān)聽來自客戶端請(qǐng)求的監(jiān)聽者
    public void listen(int port) throws Exception{
        ServerSocket ss = new ServerSocket(port);
        while(true) {
            System.out.println("等待客戶端連接...");
            Socket socket = ss.accept();
            System.out.println("客戶連接進(jìn)來了。");
            new ClientAgent(socket).start();
        }
    }
    
    class ClientAgent extends Thread{
        Socket socket;
        InputStream in;
        OutputStream out;
        public ClientAgent(Socket socket) throws Exception{
            this.socket = socket;
            in = socket.getInputStream();
            out = socket.getOutputStream();
        }
        @Override
        public void run() {
            BufferedReader in = new BufferedReader(
                                new InputStreamReader(this.in));
            PrintWriter out = new PrintWriter(this.out,true);//true自動(dòng)清除緩存   相當(dāng)于fulsh()
            try{
                out.println("text,1");
                out.println("你好歡迎使用FTP Demo!");
                while(true) {
                    String cmd = in.readLine();
                    if("?".equals(cmd)) {
                        out.println("text,1");
                        out.println("支持命令:ls,get,?,bye");
                    }
                    else if("ls".equals(cmd)) {
                        listDir(out);
                    }
                    else if(cmd.matches("^get\\s+.+")) {
                        sendFile(cmd,out,this.out);
                    }
                    else{
                        out.println("text,1");
                        out.println("不知可否!");
                    }
                }
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
        private void sendFile(String cmd, PrintWriter out,OutputStream os) throws IOException{
            String name = cmd.split("\\s+")[1];
            File file = new File(name);
            if(!file.exists()){
                out.println("text,1");
                out.println("沒有找到文件! "+name);
                return;
            }
            out.println("file,"+file.length()+","+name);
            InputStream in = new BufferedInputStream(
                                    new FileInputStream(file));
            int b;
            while((b = in.read())!=-1){
                os.write(b);
            }
            os.flush();
            in.close();
        }
        /**
         * 列出文件目錄
         * @param out
         */
        private void listDir(PrintWriter out) {
            File dir = new File(".");
            File[] files = dir.listFiles(new FileFilter() {
                public boolean accept(File pathname) {
                    return pathname.isFile();
                }
            });
            out.println("text,"+files.length+1);
            out.println("在目錄:"+dir+"中,有文件:"+files.length);
            for (File file : files) {
                out.println(file.getName());
            }
        }
    }
}
</span>

客戶端:






import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
/**
 * 
 * @author xcbeyond
 * 2014-6-7 23:16:53
 */
public class FtpClient {
    Socket socket;
    OutputStream out;
    InputStream in;
    public static void main(String[] args) throws Exception{
        FtpClient client = new FtpClient();
        client.open("localhost",8800);
    }
    public void open(String host,int port) throws Exception{
        socket = new Socket(host,port);   //對(duì)指定IP,端口的服務(wù)程序發(fā)出請(qǐng)求,建立通信
        in = socket.getInputStream();
        out =socket.getOutputStream();
        new RequestSender(out).start();
        new ResponseReceiver(in).start();
    }
    
    class RequestSender extends Thread{
        OutputStream out;
        public RequestSender(OutputStream out) {
            this.out = out;
        }
        @Override
        public void run() {
            PrintWriter out = new PrintWriter(this.out,true);
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            String str;
            try{
                while((str = in.readLine())!=null) {
                    out.println(str);
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    class ResponseReceiver extends Thread {
        InputStream in;
        public ResponseReceiver(InputStream in) {
            this.in = in;
        }
        public void run() {
            BufferedReader in = new BufferedReader(new InputStreamReader(this.in));
            try{
                String str;
                while((str = in.readLine())!=null) {
                    if(str.startsWith("text")) {
                        String num = str.substring(str.indexOf(",")+1);
                        printText(in,Integer.parseInt(num));
                    }
                    else if(str.startsWith("file")){//file,4567,filename
                        saveFile(this.in,str);
                    }
                }
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
        /**
         * 保存文件
          * @param in
         * @param size
         */
        private void saveFile(InputStream in, String head) throws IOException{
            String[] data = head.split(",");
            int length = Integer.parseInt(data[1]);
            String name  = data[2];
            OutputStream out = new BufferedOutputStream(new FileOutputStream("ftp-"+name));
             for (int i = 0; i < length; i++) {
                int b = in.read();
                out.write(b);
            }
            out.close();
            System.out.println("下載了文件:"+name);
        }
         /**
         * 打印出文本內(nèi)容
         * @param in
         * @param num
         * @throws Exception
         */
        private void printText(BufferedReader in, int num) throws Exception{
            for (int i = 0; i < num; i++) {
                System.out.println(in.readLine());
            }
        }
        
    }
}