Java學習筆記--Socket和ServerSocket


參考資料:

http://haohaoxuexi.iteye.com/blog/1979837
http://zhidao.baidu.com/link?url=OeOSa0YbOzSbMVPa8sgPXcwtyyHsWB1lPkh1XopETtNK_lVtbd9lL7NH3qlFxjC-4kNUmCkIXgcfRW7KJq9_FK
http://www.cnblogs.com/rond/p/3565113.html
http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html

TCP/IP客戶套接字

Java中有兩種類型的TCP套接字,一種用於服務器,一種用於客戶端。
ServerSocket類型設計成“監聽器”,等待客戶端連接的到來。因此,ServerSocket用於服務器。
Socket類用於客戶端,它被設計成連接服務器套接字並且初始化協議的交換。

 

圖片來自:http://www.cnblogs.com/rond/p/3565113.html

Socket

Socket可以使客戶程序與服務器程序通信,使用Socket連接服務器的過程包含以下4個基本的步驟

(1)創建Socket
(2)打開連接到Socket的輸入/輸出流
(3)按照一定協議對Socket執行讀寫操作
(4)關閉Socket

 
構造函數

Socket()

Socket(InetAddress address, int port)throws UnknownHostException, IOException
Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException
Socket(String host, int port)throws UnknownHostException, IOException
Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException
 
除去第一種不帶參數的之外,其它構造函數會嘗試建立與服務器的連接。如果失敗會拋出IOException錯誤。如果成功,則返回Socket對象。
InetAddress是一個用於記錄主機的類,其靜態getHostByName(String msg)可以返回一個實例,其靜態方法getLocalHost()也可以獲得當前主機的IP地址,並返回一個實例。Socket(String host, int port, InetAddress localAddress, int localPort)構造函數的參數分別為目標IP、目標端口、綁定本地IP、綁定本地端口。
 
Socket方法
getInetAddress();      遠程服務端的IP地址
getPort();          遠程服務端的端口
getLocalAddress()      本地客戶端的IP地址
getLocalPort()        本地客戶端的端口
getInputStream();     返回與調用的套接字相關聯的輸入流
getOutStream();      返回與調用的套接字相關聯的輸出流
值得注意的是,在這些方法里面,最重要的就是getInputStream()和getOutputStream()了。
 
Socket狀態
isClosed();            //連接是否已關閉,若關閉,返回true;否則返回false
isConnect();      //如果曾經連接過,返回true;否則返回false
isBound();            //如果Socket已經與本地一個端口綁定,返回true;否則返回false
如果要確認Socket的狀態是否處於連接中,下面語句是很好的判斷方式。
boolean isConnection=socket.isConnected() && !socket.isClosed();   //判斷當前是否處於連接

 

半關閉Socket

很多時候,我們並不知道在獲得的輸入流里面到底讀多長才結束。下面是一些比較普遍的方法:
  • 自定義標識符(譬如下面的例子,當受到“bye”字符串的時候,關閉Socket)
  • 告知讀取長度(有些自定義協議的,固定前幾個字節表示讀取的長度的)
  • 讀完所有數據
  • 當Socket調用close的時候關閉的時候,關閉其輸入輸出流

 例:

public class TestPort {
    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        String hostname  = "www.baidu.com";
        InetAddress addr = InetAddress.getByName(hostname);
        Socket so = new Socket(addr,80);
        System.out.println("遠程連接地址:"+so.getInetAddress());
        System.out.println("遠程連接端口:"+so.getPort());
        System.out.println("本地連接地址:"+so.getLocalAddress());
        System.out.println("本地連接端口:"+so.getLocalPort());
        InputStream is  = so.getInputStream();
        /*
         *  這部分為客戶端操作,需要一台遠程服務器
        int c;
        while((c=is.read())!=-1){
            System.out.println("gas");
        }*/
        is.close();
        so.close();
    }
}

運行結果:

遠程連接地址:www.baidu.com/119.75.218.70
遠程連接端口:80
本地連接地址:/192.168.1.160
本地連接端口:4338

 

 

 ServerSocket
在客戶端/服務器端的通信模式中,服務器端用來監聽特定端口上客戶端的連接。並且可以發送信息,通過ServerSocket類實現,而客戶端通過上面的Socket類實現。
 
構造函數
ServerSocket()throws IOException
ServerSocket(int port)throws IOException
ServerSocket(int port, int backlog)throws IOException
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException
 
注意點:
1. port服務端要監聽的端口;backlog客戶端連接請求的隊列長度;bindAddr服務端綁定IP
2. 如果端口被占用或者沒有權限使用某些端口會拋出BindException錯誤。譬如1~1023的端口需要管理員才擁有權限綁定。
3. 如果設置端口為0,則系統會自動為其分配一個端口;
4. bindAddr用於綁定服務器IP,為什么會有這樣的設置呢,譬如有些機器有多個網卡。
5. ServerSocket一旦綁定了監聽端口,就無法更改。ServerSocket()可以實現在綁定端口前設置其他的參數。
 
ServerSocket類可以通過getInetAddress方法返回此服務器套接字的本地地址,該方法要綁定一個本地的InetAddress,可以在構造方法 
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException 中通過參數bindAddr傳遞。
getLocalPort方法用來返回此套接字在其上偵聽的接口。
 
:返 回服務器端信息
public class GetServerIP {
    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        InetAddress addr =InetAddress.getByName("localhost");
        ServerSocket se = new ServerSocket(10000,10,addr);
        System.out.println(se.getInetAddress());
        System.out.println(se.getLocalPort());    
    }
}

運行結果

localhost/127.0.0.1
10000

 

附:完整例子

發送端 socket

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

public class SocketSender {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("127.0.0.1", 27401);
            // 向服務端發送信息
            OutputStream outer = socket.getOutputStream();
            byte[] b = "客戶端:向服務端發送文字,\"這是一行測試....\"".getBytes();
            outer.write(b);
            outer.flush();
            System.out.println("發送完畢!");

            // 接收服務端的返回值
            InputStream inner = socket.getInputStream();
            int count = 0;
            while (count == 0) {
                count = inner.available();
            }
            byte[] recv = new byte[count];
            inner.read(recv);

            String str_recv = new String(recv);
            System.out.println("客戶端:接收到服務端的文字:" + str_recv);
        } catch (IOException e) {
            System.out.println("發送端出現異常");
        } finally {
            if (socket != null)
                try {
                    socket.close();
                } catch (IOException e) {
                    System.out.println("發送端 finally 出現異常");
                }
        }
    }
}

 

接收端:ServerSocket

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

public class SocketReceiver {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(27401);
            while (true) {
                Socket socket = serverSocket.accept();

                // 接收客戶端的信息
                InputStream in = socket.getInputStream();
                int count = 0;
                while (count == 0) {
                    count = in.available();
                }
                byte[] b = new byte[count];
                in.read(b);
                String str = new String(b);
                System.out.println(str);

                // 向客戶端發送確認消息
                OutputStream outer = socket.getOutputStream();
                byte[] b_out = "已經收到,返回消息碼200".getBytes();
                outer.write(b_out);
                outer.flush();

                // 關閉socket
                socket.close();
            }
        } catch (IOException e) {
            System.out.println("接收端出現異常");
        } finally {
            if (serverSocket != null)
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    System.out.println("接收端 finally 出現異常");
                }
        }
    }
}

 

接收端:方法2,利用多線程,每一個發送端對應接收端的一個線程

public class SocketReceiverWithThread {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(27401);
            while (true) {
                Socket socket = serverSocket.accept();
                new Thread(new Handler(socket)).start();
                //socket.close(); 注意 關閉socket不能在這里,而應該寫在線程內,否則可能線程沒結束就關閉了socket
            }

        } catch (IOException e) {
            System.out.println("接收端出現異常");
        } finally {
            if (serverSocket != null)
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    System.out.println("接收端 finally 出現異常");
                }
        }
    }
}

class Handler implements Runnable {
    private Socket socket;
    public Handler(Socket socket) {
        this.socket = socket;
    }
    public void run() {
        try {
            // 接收客戶端的信息
            InputStream in = socket.getInputStream();
            int count = 0;
            while (count == 0) {
                count = in.available();
            }
            byte[] b = new byte[count];
            in.read(b);
            String str = new String(b);
            System.out.println(str);

            // 向客戶端發送確認消息
            OutputStream outer = socket.getOutputStream();
            byte[] b_out = "已經收到,返回消息碼200".getBytes();
            outer.write(b_out);
            outer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關閉socket
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

附2:使用socket進行文件傳送

客戶端 ClienPart.java

package com.client;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;

public class ClientPart {
    private Socket socket=null;
    private String ip ;// 設置成服務器IP
    private int port ;

    public ClientPart(String ip,int port) {        
        this.ip=ip;
        this.port=port;
        
    }
    private boolean createConnection() {
        try {
            socket = new Socket(ip, port);
             System.out.print("連接服務器成功!" + "\n");
             return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    //向服務器發送消息
    private void sentMessage(String msg){
        if (socket == null)
            return;
        Writer writer;
        try {
            writer = new OutputStreamWriter(socket.getOutputStream());
            writer.write(msg);  
            writer.flush();    //寫完后要記得flush  
            writer.close();  
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }  
    }
    private void getFile() {
        if (socket == null)
            return;
        DataInputStream inputStream = null;
        try {
            inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
        } catch (Exception e) {
            System.out.print("接收消息緩存錯誤\n");
            return;
        }       
        try {                        
            //本地保存路徑,文件名會自動從服務器端繼承而來。
            String savePath = "F:\\client\\";
            int bufferSize = 8192;
            byte[] buf = new byte[bufferSize];
            long len=0;
            
            savePath += inputStream.readUTF(); //讀取文件名
            DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
            len = inputStream.readLong();    //讀取文件長度
            
            System.out.println("文件的長度為:" + len);
            System.out.println("開始接收文件!");
                    
            while (true) {
                int read = 0;
                if (inputStream != null) {
                    read = inputStream.read(buf);
                }
                if (read == -1) {
                    break;
                }
                //客戶端讀出文件
                fileOut.write(buf, 0, read);
            }
            
            System.out.println("接收完成,文件存為" + savePath + "\n");
            fileOut.close();
            socket.close();
        } catch (Exception e) {
            System.out.println("接收消息錯誤" + "\n");
            return;
        }
    }
    
    private void putFile(String filename){
        String filePath = filename;
        File client_file = new File(filePath); //要傳輸的文件路徑  
        long filelen = client_file.length();
        System.out.println("要上傳的文件長度:" + (int)filelen);
        try{
            DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
            DataOutputStream out_stream = new DataOutputStream(socket.getOutputStream());
            out_stream.writeUTF(client_file.getName());
            out_stream.flush();
            out_stream.writeLong((long) client_file.length());
            out_stream.flush();

            int bufferSize = 8192;
            byte[] buf = new byte[bufferSize];

            while (true) {
                int read = 0;
                if (in_stream != null) {
                    read = in_stream.read(buf);
                }
                if (read == -1) {
                    break;
                }
                out_stream.write(buf, 0, read);
            }
            out_stream.flush();
            // 注意關閉socket鏈接,不然客戶端會等待server的數據過來,
            // 直到socket超時,會導致數據不完整。                
            in_stream.close();
            socket.close();
            System.out.println("文件傳輸完成");
        }catch(Exception e){
            e.printStackTrace();
        }
        
    }

    public static void main(String arg[]) {
       ClientPart client = new ClientPart("127.0.0.1",7005);
       client.createConnection();
       client.sentMessage("read");//向服務器發送信息,說明需要讀取文件
       client.createConnection();
       client.getFile(); //獲取服務器發送的文件
       
       client.createConnection();
       client.sentMessage("write");//向服務器發送信息,說明需要上傳文件
       client.createConnection();
       client.putFile("F:\\client\\1.txt");//向服務器發送文件

    }
}
View Code

服務端 ServerPart.java

package com.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket;


public class ServerPart {
    int port = 7005;
    public void serverStart() {        
        try {
            ServerSocket ssocket = new ServerSocket(port);
            System.out.println("服務器准備完畢!");
            while (true) {                
                String msg = checkIO(ssocket); //獲取來自客戶端的請求是讀/寫
                if(msg.compareTo("read")==0){
                    sendFile(ssocket); //向客戶端發送文件
                }
                else if(msg.compareTo("write")==0){
                    saveFile(ssocket); //存儲來自客戶端的文件
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //服務器檢查來自客戶端的請求是讀/寫
    public String checkIO(ServerSocket ssocket) throws IOException{
        Socket socket=ssocket.accept();
        Reader reader = new InputStreamReader(socket.getInputStream());
        char chars[] = new char[10];  
        int len;  
        StringBuilder sb = new StringBuilder();  
        while ((len=reader.read(chars)) != -1) {  
           sb.append(new String(chars, 0, len));  
        }
        reader.close();  
        socket.close();                        
        return sb.toString();
    }

    public void sendFile(ServerSocket ssocket) throws IOException{
        Socket soc = null;
        // 選擇進行傳輸的文件
        String filePath = "F:\\server\\info.bin";
        File myfile = new File(filePath);
        System.out.println("服務器文件長度:" + (int) myfile.length());
        soc = ssocket.accept();
        System.out.println("建立socket鏈接");

        DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
        DataOutputStream out_stream = new DataOutputStream(soc.getOutputStream());
        //將文件名及長度傳給客戶端。這里要真正適用所有平台,例如中文名的處理,還需要加工,具體可以參見Think In Java 4th里有現成的代碼。
        out_stream.writeUTF(myfile.getName());
        out_stream.flush();
        out_stream.writeLong((long) myfile.length());
        out_stream.flush();

        int bufferSize = 8192;
        byte[] buf = new byte[bufferSize];

        while (true) {
            int read = 0;
            if (in_stream != null) {
                read = in_stream.read(buf);
            }
            if (read == -1) {
                break;
            }
            out_stream.write(buf, 0, read);
        }
        out_stream.flush();
        // 注意關閉socket鏈接哦,不然客戶端會等待server的數據過來,
        // 直到socket超時,導致數據不完整。       
        out_stream.close();
        in_stream.close();
        soc.close();                
        System.out.println("服務器文件傳輸完成");
    }
    
    //服務器保存文件的方法
    public void saveFile(ServerSocket ssocket) throws IOException{
        Socket soc = ssocket.accept();
        if (soc == null)
            return;
        DataInputStream inputStream = null;
        try {
            inputStream = new DataInputStream(new BufferedInputStream(soc.getInputStream()));
        } catch (Exception e) {
            System.out.print("服務器接收消息緩存錯誤\n");
            return;
        }
        
        try {            
            //服務器文件的保存路徑
            String savePath = "f:\\server\\";
            int bufferSize = 8192;
            byte[] buf = new byte[bufferSize];
            long len=0;
            
            savePath += inputStream.readUTF(); //讀取文件名
            DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
            len = inputStream.readLong();    //讀取文件長度
            //控制台輸出
            System.out.println("文件的長度為:" + len);
            System.out.println("開始接收文件!");
            
            while (true) {
                int read = 0;
                if (inputStream != null) {
                    read = inputStream.read(buf);
                }
                if (read == -1) {
                    break;
                }
                //客戶端讀出文件
                fileOut.write(buf, 0, read);
            }
            System.out.println("接收完成,文件存為" + savePath + "\n");
            fileOut.close();
            soc.close();
        } catch (Exception e) {
            System.out.println("接收消息錯誤" + "\n");
            e.printStackTrace();
            return;
        }
    }
    
    public static void main(String arg[]) {
        //啟動服務器
        new ServerPart().serverStart();
    }
}
View Code

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM