網(wǎng)絡(luò)上的兩個程序通過一個雙向的通信連接實現(xiàn)數(shù)據(jù)的交換,這個連接的一端稱為一個socket。
建立網(wǎng)絡(luò)通信連接至少要一對端口號(socket)。socket本質(zhì)是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數(shù)據(jù)的具體形式;Socket是發(fā)動機(jī),提供了網(wǎng)絡(luò)通信的能力。
TCP/IP UDP是什么?
TCP/IP(Transmission Control Protocol/Internet Protocol)即傳輸控制協(xié)議/網(wǎng)間協(xié)議,是一個工業(yè)標(biāo)準(zhǔn)的協(xié)議集,它是為廣域網(wǎng)(WANs)設(shè)計的。UDP(User Data Protocol,用戶數(shù)據(jù)報協(xié)議)是與TCP相對應(yīng)的協(xié)議。它是屬于TCP/IP協(xié)議族中的一種。
下面是他們?nèi)叩年P(guān)系:
可以看出TCP/IP協(xié)議族包括運(yùn)輸層、網(wǎng)絡(luò)層、鏈路層。socket是一個接口,在用戶進(jìn)程與TCP/IP協(xié)議之間充當(dāng)中間人,完成TCP/IP協(xié)議的書寫,用戶只需理解接口即可。
所以socket到底是什么?
Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口。在設(shè)計模式中,Socket其實就是一個門面模式,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議。
要通過互聯(lián)網(wǎng)進(jìn)行通信,至少需要一對套接字,一個運(yùn)行于客戶機(jī)端,稱之為ClientSocket,另一個運(yùn)行于服務(wù)器端,稱之為serverSocket
socket工作原理
服務(wù)器代碼:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(2000);
System.out.println('服務(wù)器準(zhǔn)備就緒');
//System.out.println('client: '+ socket.getLocalAddress() + ' p : ' + socket.getLocalPort());
System.out.println('服務(wù)器信息 : ' +
serverSocket.getInetAddress() + ' p : ' + serverSocket.getLocalPort());
//等待客戶端連接
while (true) {
Socket client = serverSocket.accept();
ClientHandler clientHandler = new ClientHandler(client);
clientHandler.start();
}
}
private static class ClientHandler extends Thread{
private Socket socket;
ClientHandler(Socket socket){
this.socket = socket;
}
private boolean flag = true;
@Override
public void run() {
//super.run();
System.out.println('新客戶端連接: ' + socket.getInetAddress() + 'p: ' + socket.getPort());
try {
//得到打印流,用于數(shù)據(jù)輸出;服務(wù)器回送數(shù)據(jù)使用
PrintStream soketOutput = new PrintStream(socket.getOutputStream());
//得到輸入流,用于接收數(shù)據(jù)
BufferedReader socketInput = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
do {
//客戶端拿到一條數(shù)據(jù)
String str = socketInput.readLine();
//String echo = socketInput.readLine();
if ('bye'.equalsIgnoreCase(str)) {
flag = false;
soketOutput.println('bye');
}else {
//打印到屏幕,并回送數(shù)據(jù)長度
System.out.println(str);
soketOutput.println('回送: ' + str.length());
}
}while (flag);
socketInput.close();
soketOutput.close();
} catch (IOException e) {
System.out.println('連接異常斷開');
}finally {
//連接關(guān)閉
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println('客戶端已退出: ' + socket.getInetAddress() + ' p : ' + socket.getPort());
}
}
}
客戶端代碼:
import java.io.*;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket();
socket.setSoTimeout(3000);
//連接本地,端口2000;超時時間為3000ms
socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(),2000), 3000);
System.out.println('服務(wù)器已連接');
System.out.println('client: '+ socket.getLocalAddress() + ' p : ' + socket.getLocalPort());
System.out.println('服務(wù)器信息 : ' + socket.getInetAddress() + ' p : ' + socket.getPort());
try {
todo(socket);
}catch (Exception e){
System.out.println('關(guān)閉異常');
}
socket.close();
System.out.println('客戶端已退出');
}
private static void todo(Socket client) throws IOException{
InputStream in = System.in;
BufferedReader input = new BufferedReader(new InputStreamReader(in));
//得到soket輸出流,并轉(zhuǎn)換為打印流
OutputStream outputStream = client.getOutputStream();
PrintStream sockePrintStream = new PrintStream(outputStream);
// 得到sokcet輸入流,并轉(zhuǎn)換為socketBufferReader
InputStream inputStream = client.getInputStream();
BufferedReader sokcetBufferedReader = new BufferedReader(new InputStreamReader(inputStream));
boolean flag = true;
do {
//鍵盤讀取一行
String str = input.readLine();
//發(fā)送到服務(wù)器
sockePrintStream.println(str);
//從服務(wù)器讀取一行
String echo = sokcetBufferedReader.readLine();
if ('bye'.equalsIgnoreCase(echo)) {
flag = false;
}else {
System.out.println(echo);
}
}while (flag);
//關(guān)閉socket流
sockePrintStream.close();
sokcetBufferedReader.close();
}
}
執(zhí)行效果:
根據(jù)連接啟動的方式以及本地套接字要連接的目標(biāo),套接字之間的連接過程可以分為三個步驟:服務(wù)器監(jiān)聽,客戶端請求,連接確認(rèn)。
服務(wù)器監(jiān)聽:是服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實時監(jiān)控網(wǎng)絡(luò)狀態(tài)。
客戶端請求:是指由客戶端的套接字提出連接請求,要連接的目標(biāo)是服務(wù)器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字,指出服務(wù)器端套接字的地址和端口號,然后就向服務(wù)器端套接字提出連接請求。
連接確認(rèn):是指當(dāng)服務(wù)器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請求,它就響應(yīng)客戶端套接字的請求,建立一個新的線程,把服務(wù)器端套接字的描述發(fā)給客 戶端,一旦客戶端確認(rèn)了此描述,連接就建立好了。
而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài),繼續(xù)接收其他客戶端套接字的連接請求。