JAVA筆記_網路程式設計


使用I/O流作為網路間傳輸資訊的工具並使用套件import java.net

TCP/IP

為連接性的通訊協定,分別有client端,server端
並且以I/O stream為溝通管道,以byte為單位

Socket 類別

通常為client端用來連接server端的工具
socket可以透過TCP/IP連接一些常用的Internet服務,用來取得所需資訊

建立socket物件

1.
Socket s=new Socket(String, int);  //IP number , port number
2.
InetAddress serverIP=InetAddress.getByName("URL"); //會分析URL轉成IP位址
Socket s=new Socket(InetAddress, int);

取得資料

InputStream is= s.getInputStream(); //is物件可以從socket物件s中取得資訊流
OutputStream os= s.getOutputStream(); //os物件可以將資訊流寫入socket物件s

ServerSocket 類別

通常為伺服器端用來接收client端特定port的平台

創立port 9999的伺服器接收平台

ServerSocket server= new ServerSocket(9999);
while(true){
    Socket socket= server.accept(); //接收到client端的socket
    ......
}

< I/O流的觀念 >
inputstream跟outputstream之間要有buffer緩衝, 不能直接兩者傳訊

實例

Client:

  • 要知道連接server端的ip,port number
  • 建立socket以用來連接server端
  • 以I/O流發送訊息或是請求
import java.io.OutputStream;
import java.net.*;

//客戶端
public class Client{
    public static void main(String args[]){

        Socket socket=null;
        OutputStream os=null; 

        try {
            //1.要知道server端的ip以及port number
            InetAddress serverIP=InetAddress.getByName("127.0.0.1");
            int port = 9999;

            //2.創立socket連接口
            socket=new Socket(serverIP,port);

            //3.以I/O流發送訊息
            os= socket.getOutputStream();
            os.write("你好這是來自client端的資訊!".getBytes());

        } catch (Exception e) {

            e.printStackTrace();
        }
        finally{
            if(os!=null){
                try{
                    os.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
            if(socket!=null){
                try{
                    socket.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }

    }
}

server端:

  • server開啟要聆聽的port
  • 等待client端連進此port
  • 以I/O流讀取client端的訊息
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

//伺服器端
public class Server{
    public static void main(String args[]){

        ServerSocket serverSocket=null;
        Socket socket=null;
        InputStream is=null;
        ByteArrayOutputStream baos=null;


        try {
            //1.server開啟要聆聽的port
            serverSocket=new ServerSocket(9999);

            //2.等待client端連接過來,也就是client端的socket!
            socket= serverSocket.accept();

            //3.讀取客戶端的消息
            is= socket.getInputStream();
            //stream
            baos=new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while((len=is.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            System.out.println(baos.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
        finally{
            if(baos!=null){
                try{
                    baos.close();
                }catch(Exception e){
                    e.printStackTrace();
                }

            }
            if(is!=null){
                try{
                    is.close();
                }catch(Exception e){
                    e.printStackTrace();
                }

            }
            if(socket!=null){
                try{
                    socket.close();
                }catch(Exception e){
                    e.printStackTrace();
                }

            }
            if(serverSocket!=null){
                try{
                    serverSocket.close();
                }catch(Exception e){
                    e.printStackTrace();
                }

            }
        }

    }
}

實例二 - 文建傳輸

1.我們首先啟用server, server端等待client端對接 <卡在serverSocket.accept()>
2.client端啟動
3.server端對接到client端資訊

client

import java.io.*;
import java.net.*;

public class File_Client {
    public static void main(String args[]) throws Exception{

        //1. 創立socket
        Socket socket =new Socket(InetAddress.getByName("127.0.0.1"),9000);

        //2. 創立output stream
        OutputStream os=socket.getOutputStream();

        //3. 文件流
        FileInputStream  fis=new FileInputStream(new File("monkey.jpg"));

        //4. 寫出文件
        byte[] buffer=new byte[1024];
        int len;
        while((len=fis.read(buffer))!=-1){
            os.write(buffer,0,len);
        }

        //關閉資源
        fis.close();
        os.close();
        socket.close();
    }
}

server

import java.io.*;
import java.net.*;

public class File_Server{

    public static void main(String args[]) throws Exception{

        //1. 創立服務端口
        ServerSocket serverSocket=new ServerSocket(9000);

        //2. 監聽客戶端的連接
        Socket socket=serverSocket.accept(); //一直迴圈直到接收到

        //3. 獲取客戶端輸出流
        InputStream is= socket.getInputStream();

        //4. 文件輸出
        FileOutputStream fos=new FileOutputStream(new File("receive.jpg"));
        byte[] buffer=new byte[1024];
        int len;
        while((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }

        //5. 關閉資源
        fos.close();
        is.close();
        socket.close();
        serverSocket.close();

    }
}

但是其實還是有點問題,例如客戶端應該要確定伺服器端對接到訊息才斷開
而伺服器端也要回覆客戶端已經接收完畢

客戶端

import java.io.*;
import java.net.*;

public class File_Client {
    public static void main(String args[]) throws Exception{

        //1. 創立socket
        Socket socket =new Socket(InetAddress.getByName("127.0.0.1"),9000);

        //2. 創立output stream
        OutputStream os=socket.getOutputStream();

        //3. 文件流
        FileInputStream  fis=new FileInputStream(new File("monkey.jpg"));

        //4. 寫出文件
        byte[] buffer=new byte[1024];
        int len;
        while((len=fis.read(buffer))!=-1){
            os.write(buffer,0,len);
        }

        //5. 通知服務端已經傳輸完了
        socket.shutdownOutput(); //client端傳輸完畢

        //6. 確定server端成功對接才斷開連線
        InputStream inputstream=socket.getInputStream();
        //String byte[]
        ByteArrayOutputStream baso=new ByteArrayOutputStream();

        byte[] buffer2=new byte[1024];
        int len2;
        while((len2=inputstream.read(buffer2))!=-1){
            baso.write(buffer2,0,len2);
        }

        System.out.println(baso.toString());

        //關閉資源
        baso.close();
        inputstream.close();
        fis.close();
        os.close();
        socket.close();
    }
}

伺服器端

import java.io.*;
import java.net.*;

public class File_Server{

    public static void main(String args[]) throws Exception{

        //1. 創立服務端口
        ServerSocket serverSocket=new ServerSocket(9000);

        //2. 監聽客戶端的連接
        Socket socket=serverSocket.accept(); //一直迴圈直到接收到

        //3. 獲取客戶端輸出流
        InputStream is= socket.getInputStream();

        //4. 文件輸出
        FileOutputStream fos=new FileOutputStream(new File("receive.jpg"));
        byte[] buffer=new byte[1024];
        int len;
        while((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }

        //5. 通知客戶端成功對接
        OutputStream os= socket.getOutputStream();
        os.write("connect successful!".getBytes());

        // 關閉資源
        fos.close();
        is.close();
        socket.close();
        serverSocket.close();

    }
}

<問題與討論>
1.其實client/server的區分並非是用誰是資料給予方(server) 誰是資料接收方(client)
而是client是發出連線請求的端點, server是接收連線請求的端點,至於連線之後資料要由誰給誰都行,此混淆的觀念要注意

  1. 寫出文件的程式碼說明:
    資訊在socket中的stream傳遞時其實都是採用byte的資料格式傳輸
    因此假設你要將資料由socket傳遞給server端
    或是需要print顯示傳過來的資訊,都要將資料內容(byte)寫進outputstream

<情況1>

byte[] buffer=new byte[1024];
        int len;
        while((len=fis.read(buffer))!=-1){
            os.write(buffer,0,len);
        }

<情況2>

byte[] buffer2=new byte[1024];
int len2;
while((len2=inputstream.read(buffer2))!=-1){
        baso.write(buffer2,0,len2);
}

        System.out.println(baso.toString());

inputstream.read(buffer) 會將inputstream資料流存進buffer,並且返回buffer存完資料後的內容長度
再由outputstream os寫入buffer存下的資訊
爾後os可以用作socket outputstream傳輸或是print顯示

inputstream --> buffer(byte[])
len=fis.read(buffer)
buffer(byte[]) --> outputstream
os.write(buffer,0,len);


UDP

沒有client/server的概念,只有簡單分為接收端與傳輸端:

  • socket在兩者間沒有分別
  • 傳送者不在乎是否有成功對接
  • 傳給他人的封包跟從他人那邊接收的封包,都是是DatagramPacket物件處理

DatagramSocket 類別

不論是sender,receiver 都要建立
DatagramSocket socket = new DatagramSocket(port number)

DatagramPacket 類別

不論是sender,receiver 都要建立
sender是用來傳送, 一開始創立就有內容

DatagramPacket packet =DatagramPacket(str.getByte(),str.length(),IPadrress,port);
senderSocket.send(packet);
sendSocket.close();

receiver用來接收,因為一開始創立的物件是沒內容的,要先建立緩衝空間

byte[]  buffer =new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer,1024); //空
receiverSocket.receive(packet) //將獲取資料寫進packet

String str= new String(packet.getData(),0,packet.length);
System.out.println(str);

receiverSocket.close();

範例

傳輸端


import java.net.*;

public class UDPclient {
    public static void main(String args[]) throws Exception{

        //1. 創立一個socket
        DatagramSocket socket=new DatagramSocket();

        //2. 建立package
        String msg="the message to server!";
        InetAddress localhost= InetAddress.getByName("localhost");
        int port= 9090;

        //打包 (包裡的東西 給誰)
        DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,localhost,port);

        //3. send package
        socket.send(packet);  //沒人對接也不管

        //4. 關閉資源
        socket.close();

    }
}

接收端

import java.io.*;
import java.net.*;


//還是要等待客戶端的連接
public class UDPserver {
    public static void main(String args[]) throws Exception{

        //1. 開放端口
        DatagramSocket socket =new DatagramSocket(9090);

        //2. 接收package
        byte[] buffer=new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, 0,buffer.length);
        socket.receive(packet);  //loop直到接收

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(new String(packet.getData(),0,packet.getLength()));

        //3. 關閉資源
        socket.close();
    }
}

單邊聊天室實作

sender可以一直傳訊息給receiver,直到特殊條件後停止

sender

import java.io.*;
import java.net.*;
public class UDPchat_sender {
    public static void main(String args[]) throws Exception{

        DatagramSocket socket=new DatagramSocket(9000);

        //將輸入值打包成數據
        BufferedReader reader= new BufferedReader(new InputStreamReader(System.in));

        while(true){
            String data = reader.readLine();
            byte[] datas = data.getBytes();
            DatagramPacket packet = new DatagramPacket(datas,0, datas.length,new InetSocketAddress("localhost", 9001));
            socket.send(packet);

            if(data.equals("bye")) break;
        }

        socket.close();
    }
}

receiver

import java.net.*;
public class UDPchat_receiver {
    public static void main(String args[]) throws Exception{

        DatagramSocket socket = new DatagramSocket(9001);

        while(true){

            byte[] input= new byte[1024];
            DatagramPacket packet =new DatagramPacket(input,0,input.length);
            socket.receive(packet);

            byte[] datas=packet.getData();
            String receiveData=new String(datas,0,datas.length);

            System.out.println(receiveData);

            if(receiveData.equals("bye")) break;

        }

        socket.close();

    }
}

java 11 12影片等補完thread後來看







你可能感興趣的文章

[29] 文法 - 運算子優先序、結合性

[29] 文法 - 運算子優先序、結合性

DAY38:Duplicate Encoder

DAY38:Duplicate Encoder

CS50 IP (Internet Protocol)

CS50 IP (Internet Protocol)






留言討論