Java實現TCP通信-----Scoket類和ServiceScoket類------使用ServiceScoket類模擬服務器

目錄

 

1、概述

2、Scoket類

3、ServiceScoket類

4、簡單的客戶端服務端通信代碼 

4-1、服務器端

4-2、客戶端

5、模擬B\S服務器

總結:


1、概述

TCP通信能實現兩臺計算機之間的數據交互,通信的兩端,要嚴格區分爲客戶端(Client)與服務端(Server)。Java提供了兩個類用於TCP通信程序

1、客戶端:java.new.Scoket類。創建Scoket對象,向服務器發出連接請求,服務器端響應請求,兩者建立連接開始通信

2、服務器端:java.net.ServiceScoket。創建ServiceScoket對象,相當於開一個服務,並等待客戶端的連接

2、Scoket類

該類實現客戶端套接字,套接字指的是設備之間通信的端點

構造方法

public Socket(String host, int port) :創建套接字對象並將其連接到指定主機上的指定端口號。如果指 定的host是null ,則相當於指定地址爲回送地址

 構造方法使用舉例

Socket client = new Socket("127.0.0.1", 10001);

成員方法

public InputStream getInputStream() : 返回此套接字的輸入流。

如果此Scoket具有相關聯的通道,則生成的InputStream 的所有操作也關聯該通道。 關閉生成的InputStream也將關閉相關的Socket。

public OutputStream getOutputStream() : 返回此套接字的輸出流。

如果此Scoket具有相關聯的通道,則生成的OutputStream 的所有操作也關聯該通道。 關閉生成的OutputStream也將關閉相關的Socket。

public void close() :關閉此套接字。

一旦一個socket被關閉,它不可再使用。 關閉此socket也將關閉相關的InputStream和OutputStream 。

public void shutdownOutput() : 禁用此套接字的輸出流。  任何先前寫出的數據將被髮送,隨後終止輸出流

3、ServiceScoket類

 該類實現了服務器套接字,該對象等待接收通過網絡的請求

構造方法

public ServerSocket(int port) :使用該構造方法在創建ServerSocket對象時,就可以將其綁定到一個指 定的端口號上,參數port就是端口號

構造方法使用舉例

ServerSocket server = new ServerSocket(10001);

 成員方法:

public Scoket accept():偵聽並接受連接,返回一個新的Scoket對象,用於和客戶端shi實現通信。該方法會一直處於阻塞態,直到建立連接。

4、簡單的客戶端服務端通信代碼 

無論是客戶端向服務器端寫數據,還是服務器回寫給客戶端數據都是一些流的傳遞,所以在這裏接收數據也好,發送數據也好,都可以使用四類流對象及其子類進行操作(四類流對象:InputStream,OutputStream,Reader,Writer)。

4-1、服務器端

package mytest.newChat;

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

public class Server {
    public static void main(String[] args) throws IOException {
        //創建ServiceScoket對象,並設置端口號爲9961
        ServerSocket ss = new ServerSocket(9961);
        Scanner sc = new Scanner(System.in);
        //創建一個無限循環,來保證可以循環發送和接受數據
        while (true) {
            System.out.println("================讀===================");
            //調用ServiceScoket的accept方法,接收連接,並返回Scoket
            Socket s = ss.accept();
            //獲取客戶端發送過來的信息
            InputStream is = s.getInputStream();
            //翻譯用戶發送的信息,並打印在控制檯
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = is.read(bytes)) != -1) {
                System.out.println(new String(bytes, 0, len));
            }
            //下面服務器回寫給客戶端的操作
            System.out.println("================回寫================");
            //調用Scoket的getOupputStream()方法,返回此服務器的輸出流
            OutputStream os = s.getOutputStream();
            //輸入要回寫的信息
            String str = sc.next().trim();

            String strClient = "服務器端:" + str;
            //將要回寫的信息翻譯成字節流並回寫
            os.write(strClient.getBytes());

            //關閉資源
            os.close();
            is.close();

        }

    }
}

4-2、客戶端

package mytest.newChat;

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

public class Client {
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        while (true){
            //創建Scoket對象,並設置要訪問的服務的IP和端口號
            Socket socket = new Socket("localhost", 9961);
            System.out.println("================寫================");
            //返回此客戶端的一個輸出流
            OutputStream os = socket.getOutputStream();
            String str = sc.next().trim();
            String strClient = "客戶端:" + str;
            //將客戶要發送的信息翻譯成字節文件,發送出去
            os.write(strClient.getBytes());
            //發送一個終結符,告訴服務器,已經發送完畢
            socket.shutdownOutput();
            //以下是獲取服務器回寫的數據
            System.out.println("================讀================");
            //返回此客戶端的一個輸入流
            InputStream is = socket.getInputStream();
            //將服務器回寫的數據進行翻譯,並打印在控制檯
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = is.read(bytes)) != -1) {
                System.out.println(new String(bytes, 0, len));
            }
            //關閉資源
            is.close();
            os.close();
        }



    }
}

5、模擬B\S服務器

在寫代碼之前,我先將事先寫好的前端頁面放在當前項目下面,這也代表將web項目放在了服務器下面

我們先來分析一下瀏覽器是如何訪問到服務器上的web項目的

1、首先,瀏覽器本身就是一個客戶端,其向服務端發送訪問的請求,而服務端獲取來自客戶端的請求,查找服務器上是否有客戶端所需要的數據,在進一步將客戶端所需要的數據回寫給客戶端,也就是瀏覽器。

我們先簡單的寫一個服務器端程序來獲取瀏覽器發送的請求,看下瀏覽器發送的是什麼!

服務端代碼(5-1)

package mytest.htmlSet;

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

/**
 * @Author: ${user}
 */
public class ServiceNew {
    public static void main(String[] args) throws IOException {
        //創建ServiceSocket類,並設置端口號爲1004
        ServerSocket ss = new ServerSocket(1004);
        //接受來自瀏覽器的信息
        Socket accept = ss.accept();
        //返回服務器的一個輸入流
        InputStream is = accept.getInputStream();
        //翻譯來自瀏覽器的請求並打印在控制檯
        int len ;
        byte[] bytes = new byte[1024];
        while ((len=is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }
    }
}

運行服務器代碼,並在瀏覽器中輸入以下網址:

http://127.0.0.1:1004/

注:127.0.0.1是本地網址,1004是服務端口號,這個端口號在創建ServiceScoket對象時已經設置,當輸入網址並點擊確認後,瀏覽器會自動定位到127.0.0.1(本地)的ServiceNew中。而服務器端代碼會在在控制檯打印下面的內容,一下內容就是瀏覽器向服務器發送的信息。爲了方便解讀,我將每行信息前標註了序號。

1、GET / HTTP/1.1
2、Host: 127.0.0.1:1004
3、Connection: keep-alive
4、Cache-Control: max-age=0
5、Upgrade-Insecure-Requests: 1
6、User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
7、Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
8、Accept-Encoding: gzip, deflate, br
9、Accept-Language: zh-CN,zh;q=0.9
 

 第一行:GET / HTTP/1.1  這一行有三個信息。GET是瀏覽器以何種方式訪問,有GET和POST之分,緊接着的 “/” 這個符號其實是l路徑分隔符,單個的路徑分隔符就是根目錄的意思,HTTP/1.1是超文本傳輸協議,每個網站都要遵守的協議。總的來說這一行,瀏覽器告訴服務器的信息是,“我”(瀏覽器)要以GET的方式,在按照HTTP/1.1協議的情況下,訪問你服務器根目錄的空文件。如果將原本的網址寫成http://127.0.0.1:1004/web,此時的第一行就會變成GET /web HTTP/1.1 。

第二行:是訪問的服務號和端口號

後面幾行都是瀏覽器信息和和訪問的要求,在這裏不再贅述!

其實分析到這,如果服務器獲取瀏覽器發送的請求信息中的第一行,獲取,瀏覽器要訪問的路徑,就可以把服務器上的信息回寫出去。獲取瀏覽器發送的信息的第一行,可以使用BufferReader的readLine方法獲取,然後進行切割,從而獲取瀏覽器要訪問的內容的路徑。

 給瀏覽器回寫內容,需要在服務器代碼中寫下如下代碼。告訴瀏覽器訪問成功了,我將以文本形式輸出。

os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());

我們先試着在瀏覽器返回一段話:

package mytest.htmlSet;

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

/**
 * @Author: ${user}
 */
public class ServiceNew {
    public static void main(String[] args) throws IOException {
        //創建ServiceSocket類,並設置端口號爲1004
        ServerSocket ss = new ServerSocket(1004);
        //接受來自瀏覽器的信息
        Socket accept = ss.accept();
        //返回服務器的一個輸入流
        InputStream is = accept.getInputStream();
        //翻譯來自瀏覽器的請求並打印在控制檯
        int len ;
        byte[] bytes = new byte[1024];
//        while ((len=is.read(bytes))!=-1){
//            System.out.println(new String(bytes,0,len));
//        }
        len = is.read(bytes);
        System.out.println(new String(bytes));

        OutputStream os = accept.getOutputStream();
        os.write("HTTP/1.1 200 OK\r\n".getBytes());
        os.write("Content-Type:text/html\r\n".getBytes());
//        os.write("Accept-Language:UTF-8".getBytes());
        os.write("\r\n".getBytes());

        os.write("啥?".getBytes());
    }
}

在瀏覽器輸入網址和端口號後成功顯示“啥?”

接下來我們就動手完成B\S服務器的代碼

package mytest.htmlSet;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Author: ${user}
 */
public class ServiceNew {
    public static void main(String[] args) throws IOException {
        //創建ServiceSocket類,並設置端口號爲1004
        ServerSocket ss = new ServerSocket(1004);
//創建無限循環,重複開啓線程獲取圖片信息
        while (true) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //接受來自瀏覽器的信息
                        Socket accept = ss.accept();
                        //返回服務器的一個輸入流
                        InputStream is = accept.getInputStream();
                        // 創建BufferedReader並調用readLine方法獲取瀏覽器請求的第一行
                        BufferedReader br = new BufferedReader(new InputStreamReader(is));
                        String s = br.readLine();
//        System.out.println(s);
                        //調用String的split方法分隔獲取到的第一行的數據返回一個數組,並獲取第二個元素,並去掉第一個路徑分隔符
                        String s1 = s.split(" ")[1].substring(1);
                        System.out.println(s1);
                        //創建File對象指向瀏覽器要訪問的路徑
                        File file = new File(s1);
                        //判斷是否存在---測試
                        System.out.println(file.exists());
                        //創建緩衝流對象,讀取路徑下下的信息
                        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                        //獲取輸出流對象,把路徑下的內容寫回去
                        OutputStream os = accept.getOutputStream();
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());
                        os.write("Content-Type:text/html\r\n".getBytes());
                        os.write("\r\n".getBytes());
                        byte[] bytes = new byte[1024];
                        int len = 0;
                        while ((len = bis.read(bytes)) != -1) {
                            os.write(bytes, 0, len);
                        }
                        //關閉資源
                        os.close();
                        bis.close();
                        br.close();
                        is.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }


    }


}

在瀏覽器輸入http://127.0.0.1:1004/tes_net/web/index.html,即可訪問web項目

總結:

能力尚淺,有待進步,如有不足,不吝賜教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章