目錄
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));
}
}
}
運行服務器代碼,並在瀏覽器中輸入以下網址:
注: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項目
總結:
能力尚淺,有待進步,如有不足,不吝賜教!