目錄
基本概念
發(fā)送端與接收端
請(qǐng)求與響應(yīng)
?編輯客戶端與服務(wù)器
Socket套接字?
分類
數(shù)據(jù)報(bào)套接字
流套接字傳輸模型??
UDP數(shù)據(jù)報(bào)套接字編程
DatagramSocket API
DatagramPacket API
InetSocketAddress API
示例一:
示例二:
TCP流數(shù)據(jù)報(bào)套接字編程
ServerSocket API
Socket API
示例一:
?
網(wǎng)絡(luò)編程指的是,網(wǎng)絡(luò)上的主機(jī)的不同進(jìn)程通過編程的方式實(shí)現(xiàn)網(wǎng)絡(luò)通信.同一主機(jī)下只要滿足不同進(jìn)程間的通信就可以成為"網(wǎng)絡(luò)通信"
基本概念
發(fā)送端與接收端
在網(wǎng)絡(luò)通信中:
作為發(fā)送數(shù)據(jù)的進(jìn)程稱為"發(fā)送端",發(fā)送端主機(jī)即網(wǎng)絡(luò)通信中的"源主機(jī)"?
作為接收數(shù)據(jù)的進(jìn)程稱為"接收端",接收端主機(jī)即網(wǎng)絡(luò)通信中的"目的主機(jī)"
注意:網(wǎng)絡(luò)通信中的發(fā)送端與接收端都是相對(duì)的.
請(qǐng)求與響應(yīng)
一般來說,一次網(wǎng)絡(luò)通信中設(shè)計(jì)到兩次數(shù)據(jù)傳輸:
- 第一次:A端向B端發(fā)送的請(qǐng)求
- 第二次:B端向A端發(fā)送的響應(yīng)
客戶端與服務(wù)器
服務(wù)器:在網(wǎng)絡(luò)通信下,提供服務(wù)的一端.(服務(wù)可以指:響應(yīng)一定的要求)
客戶端:獲取服務(wù)的一端
Socket套接字?
Socket套接字,是由系統(tǒng)提供用于網(wǎng)絡(luò)通信的技術(shù),是基于TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元.基于Socket套接字的網(wǎng)絡(luò)程序開發(fā)就是網(wǎng)絡(luò)編程.
分類
套接字根據(jù)傳輸層協(xié)議主要分成:
- 數(shù)據(jù)報(bào)套接字:使用傳輸層UDP協(xié)議(User Datagram Protocol)用戶數(shù)據(jù)報(bào)協(xié)議
- 流套接字:使用傳輸層TCP協(xié)議(Transmission Control Protocol)傳輸層控制協(xié)議
- 原始套接字:用于自定義傳輸層協(xié)議
數(shù)據(jù)報(bào)套接字
數(shù)據(jù)報(bào)固定每次傳輸?shù)淖止?jié),更像是寫信,有來有回的.
流套接字傳輸模型??
面對(duì)的是字節(jié)流.
打電話一般,接通后就可以無節(jié)制的傳輸.
UDP數(shù)據(jù)報(bào)套接字編程
DatagramSocket API
構(gòu)造方法
方法簽名 | 方法說明 |
DatagramSocket() | 創(chuàng)建一個(gè)UDP數(shù)據(jù)報(bào)套接字的Socket,綁定到本機(jī)任意一個(gè)隨機(jī)端口 (一般用于客戶端) |
DatagramSocket(int port) |
創(chuàng)建一個(gè)UDP數(shù)據(jù)報(bào)套接字的Socket,綁定到本機(jī)指定的端口(一般用 于服務(wù)端) |
常用方法?
方法 | 方法說明 |
void receive(DatagramPacket p) |
從此套接字接收數(shù)據(jù)報(bào)(如果沒有接收到數(shù)據(jù)報(bào),該方法會(huì)阻 塞等待) |
void send(DatagramPacket p) |
從此套接字發(fā)送數(shù)據(jù)報(bào)包(不會(huì)阻塞等待,直接發(fā)送) |
void close() | 關(guān)閉此數(shù)據(jù)報(bào)套接字 |
DatagramPacket API
構(gòu)造方法
方法簽名 | 方法說明 |
DatagramPacket(byte[] buf, int length) |
構(gòu)造一個(gè)DatagramPacket以用來接收數(shù)據(jù)報(bào),接收的數(shù)據(jù)保存在 字節(jié)數(shù)組(第一個(gè)參數(shù)buf)中,接收指定長(zhǎng)度(第二個(gè)參數(shù) length) |
DatagramPacket(byte[] buf, int length, SocketAddress address) |
構(gòu)造一個(gè)DatagramPacket以用來發(fā)送數(shù)據(jù)報(bào),發(fā)送的數(shù)據(jù)為字節(jié) 數(shù)組(第一個(gè)參數(shù)buf)中,從0到指定長(zhǎng)度(第二個(gè)參數(shù) length)。address指定目的主機(jī)的IP和端口號(hào) |
常用方法?
方法簽名 | 方法說明 |
InetAddress getAddress() |
從接收的數(shù)據(jù)報(bào)中,獲取發(fā)送端主機(jī)IP地址;或從發(fā)送的數(shù)據(jù)報(bào)中,獲取 接收端主機(jī)IP地址 |
int getPort() | 從接收的數(shù)據(jù)報(bào)中,獲取發(fā)送端主機(jī)的端口號(hào);或從發(fā)送的數(shù)據(jù)報(bào)中,獲 取接收端主機(jī)端口號(hào) |
byte[] getData() | 獲取數(shù)據(jù)報(bào)中的數(shù)據(jù) |
InetSocketAddress API
InetSocketAddress是ScketAddress的一個(gè)子類,用來包裝IP與端口號(hào)
方法 | 方法說明 |
InetSocketAddress(InetAddress addr, int port) | 創(chuàng)建一個(gè)Socket地址,包含IP地址和端口號(hào) |
示例一:
客戶端像服務(wù)器發(fā)出請(qǐng)求,但服務(wù)器無響應(yīng)版本
服務(wù)器:
public class UdpServer {
private DatagramSocket socket= null;
public UdpServer(int port) throws SocketException {//構(gòu)造方法
this.socket = new DatagramSocket(port);
}
public void start() throws IOException {//作為啟動(dòng)服務(wù)器的方法
while(true){//因?yàn)椴恢朗裁磿r(shí)候客戶端會(huì)發(fā)送請(qǐng)求
//作為服務(wù)器,需要不停的接收客戶端的請(qǐng)求
//創(chuàng)建packet
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);//用bytes作為接收,使用的長(zhǎng)度為bytes的長(zhǎng)度
System.out.println("等待接收數(shù)據(jù)中...");
socket.receive(packet);//還沒收到之前會(huì)進(jìn)行阻塞等待
//此處的版本沒有作出響應(yīng)
//我們可以打印出收到的packet中的數(shù)據(jù)看看有什么東西
System.out.println("IP: " + packet.getAddress().getHostAddress());
System.out.println("端口號(hào): " + packet.getPort());
System.out.printf("文本數(shù)據(jù)為: " + new String(packet.getData()));
System.out.println("原始數(shù)據(jù)為: " + Arrays.toString(packet.getData()));
}
}
public static void main(String[] args) throws IOException {
UdpServer udpServer = new UdpServer(1024);
udpServer.start();
}
}
客戶端:
方法一:
public class UdpClient {
public static void main(String[] args) throws IOException {
//創(chuàng)建Socket
DatagramSocket socket = new DatagramSocket();//創(chuàng)建一個(gè)socket,端口號(hào)為系統(tǒng)隨機(jī)分配
//構(gòu)建Packet
byte[] bytes = "Hello World".getBytes();//字符串轉(zhuǎn)換成byte再塞進(jìn)數(shù)組
SocketAddress address = new InetSocketAddress("localhost",1024);//目的IP為本地地址,端口號(hào)為1024
DatagramPacket packet = new DatagramPacket(bytes,bytes.length,address);//構(gòu)建packet
socket.send(packet);//發(fā)送
System.out.println("發(fā)送完成");
}
}
方法二:
public class UdpClient {
private DatagramSocket socket = null;//socket
private String serverIp;
private int serverPort;
public UdpClient(String serverIp,int serverPort) throws SocketException {
this.socket = new DatagramSocket();
this.serverIp = serverIp;
this.serverPort = serverPort;
}
public void start() throws IOException {
System.out.println("客戶端啟動(dòng)");
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("輸入:");
String text = scanner.next();
if(text.equals("exit")){
System.out.println("再見");
break;
}
//需要用InetAddress將字符串鐘的IP轉(zhuǎn)換成地址形式
//SocketAddress address = new InetSocketAddress("localhost",1024);//也可以創(chuàng)建一個(gè)實(shí)例進(jìn)行包裝IP與端口號(hào)
//此處的長(zhǎng)度是字節(jié)的長(zhǎng)度噢,注意單位
DatagramPacket packet = new DatagramPacket(text.getBytes(),text.getBytes().length,InetAddress.getByName(serverIp),serverPort);
socket.send(packet);
System.out.println("發(fā)送成功");
}
}
public static void main(String[] args) throws IOException {
UdpClient client = new UdpClient("127.0.0.1",1024);
client.start();
}
先啟動(dòng)服務(wù)器后啟動(dòng)客戶端發(fā)送.
記得打開IDEA可以同時(shí)運(yùn)行兩個(gè)進(jìn)程的選項(xiàng)噢!
服務(wù)器接收到的信息為:
示例二:
做一個(gè)服務(wù)器對(duì)客戶端有響應(yīng)的版本
簡(jiǎn)單的英漢翻譯
服務(wù)器:
public class UdpServerResponse{
private DatagramSocket socket= null;
public UdpServerResponse(int port) throws SocketException {//構(gòu)造方法
this.socket = new DatagramSocket(port);
}
public void start() throws IOException {//啟動(dòng)服務(wù)器
while(true){
byte[] bytes = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(bytes,bytes.length);//創(chuàng)建包來接收
System.out.println("等待接收數(shù)據(jù)中...");
socket.receive(receivePacket);//接收包
String request = new String(receivePacket.getData(),0,receivePacket.getLength());//根據(jù)接收到的包轉(zhuǎn)換成字符串
String response = process(request);//對(duì)請(qǐng)求進(jìn)行分析
//記得是getSocketAddress噢里面通常包含了IP與端口號(hào)
DatagramPacket sendPacket = new DatagramPacket(response.getBytes(),response.getBytes().length,receivePacket.getSocketAddress());
socket.send(sendPacket);//對(duì)客戶端作出響應(yīng)
System.out.println("客戶端IP: " + receivePacket.getAddress());
System.out.println("客戶端端口號(hào): " + receivePacket.getPort());
System.out.println("收到的文本: " + request);
System.out.println("返回的文本: " + response);
}
}
public String process(String request){//解析請(qǐng)求,看看要做什么
//這里就做一個(gè)英漢翻譯吧
HashMap<String,String> map = new HashMap<>();
map.put("人","human");
map.put("貓","cat");
map.put("狗","dog");
return map.getOrDefault(request,"查閱失敗");
}
public static void main(String[] args) throws IOException {
UdpServerResponse udpServerResponse = new UdpServerResponse(1024);
udpServerResponse.start();
}
}
客戶端:
public class UdpClientResponse {
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
public UdpClientResponse(String serverIp,int serverPort) throws SocketException {
this.serverIp = serverIp;
this.serverPort = serverPort;
socket = new DatagramSocket();
}
public void start() throws IOException {
System.out.println("客戶端啟動(dòng)");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("輸入: ");
String request = scanner.next();
if(request.equals("exit")){
System.out.println("再見!");
break;
}
//根據(jù)請(qǐng)求創(chuàng)建包
DatagramPacket sendPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName("127.0.0.1"),1024);
socket.send(sendPacket);
System.out.println("發(fā)送成功");
DatagramPacket receivePacket = new DatagramPacket(new byte[1024],1024);//創(chuàng)建接收包
socket.receive(receivePacket);
String receive = new String(receivePacket.getData(),0,receivePacket.getLength());
System.out.println(receive);
}
}
public static void main(String[] args) throws IOException {
UdpClientResponse udpClientResponse = new UdpClientResponse("127.0.0.1",1024);
udpClientResponse.start();
}
}
服務(wù)器的打印
客戶端的打印
TCP流數(shù)據(jù)報(bào)套接字編程
ServerSocket API
創(chuàng)建TCP服務(wù)端的API
構(gòu)造方法
構(gòu)造方法 | 方法說明 |
ServerSocket(int port) | 創(chuàng)建一個(gè)服務(wù)端流套接字Socket,并綁定到指定端口 |
方法
方法簽 名 |
方法說明 |
Socket accept() |
開始監(jiān)聽指定端口(創(chuàng)建時(shí)綁定的端口),有客戶端連接后,返回一個(gè)服務(wù)端Socket 對(duì)象,并基于該Socket建立與客戶端的連接,否則阻塞等待 |
void close() |
關(guān)閉此套接字 |
Socket API
用來建立鏈接后保存對(duì)方的信息
構(gòu)造方法:
方法 | 方法說明 |
Socket(String host, int port) |
創(chuàng)建一個(gè)客戶端流套接字Socket,并與對(duì)應(yīng)IP的主機(jī)上,對(duì)應(yīng)端口的 進(jìn)程建立連接 |
常用方法?
方法簽名 | 方法說明 |
InetAddress getInetAddress() | 返回套接字所連接的地址 |
InputStream getInputStream() | 返回此套接字的輸入流 |
OutputStream getOutputStream() | 返回此套接字的輸出流 |
在TCP協(xié)議中的連接還分為長(zhǎng)連接與短鏈接.
- 短連接:每次接收到數(shù)據(jù)并返回響應(yīng)后,都關(guān)閉連接,即是短連接。也就是說,短連接只能一次收發(fā)數(shù)據(jù)
- 長(zhǎng)連接:不關(guān)閉連接,一直保持連接狀態(tài),雙方不停的收發(fā)數(shù)據(jù),即是長(zhǎng)連接。也就是說,長(zhǎng)連接可以多次收發(fā)數(shù)據(jù)
示例一:
一請(qǐng)求一響應(yīng)
此處為長(zhǎng)連接(把代碼里的while(true)去掉就是短連接啦!只進(jìn)行一次請(qǐng)求響應(yīng))
服務(wù)器:
對(duì)于服務(wù)器來說,每次與客戶端連接后會(huì)創(chuàng)建一個(gè)socket來暫時(shí)存儲(chǔ)客戶端的信息數(shù)據(jù)
斷開連接后,記得要將這個(gè)存儲(chǔ)客戶端數(shù)據(jù)的socket進(jìn)行close釋放掉
在服務(wù)器進(jìn)程中,一個(gè)客戶端socket會(huì)占用文件描述符的一個(gè)位置,一個(gè)服務(wù)器可能會(huì)要與成千上萬個(gè)客戶端進(jìn)行通信,不釋放就會(huì)將文件描述符的位置沾滿造成泄露.
而服務(wù)器的serverSocket的生命周期與整個(gè)進(jìn)程相當(dāng),且只有一個(gè).所以可以不進(jìn)行釋放
使用線程池,用多線程的方式來運(yùn)行服務(wù)器達(dá)到同時(shí)與多個(gè)客戶端進(jìn)行通信的功能.
public class TcpServer {
private ServerSocket socket = null;
public TcpServer(int port) throws IOException {
socket = new ServerSocket(port);
}
public void start() throws IOException {
//嘗試鏈接
ExecutorService threadPool = Executors.newCachedThreadPool();//創(chuàng)建一個(gè)線程池,一個(gè)線程對(duì)應(yīng)一個(gè)客戶端進(jìn)行通信
while (true) {
Socket clientSocket = socket.accept();//會(huì)阻塞等待接受
threadPool.submit(() -> {//向線程提供任務(wù)
try {
processConnect(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
public void processConnect(Socket clientSocket) throws IOException {
System.out.println("已與客戶端進(jìn)行鏈接-" + clientSocket.getInetAddress() + clientSocket.getPort());
try(InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()){
while(true){
Scanner scanner = new Scanner(inputStream);//讀
PrintWriter printWriter = new PrintWriter(outputStream);//寫
if(!scanner.hasNext()){//客戶端不再傳輸數(shù)據(jù)就斷開鏈接
System.out.println("結(jié)束");
break;
}
String request = scanner.next();//接收請(qǐng)求
String response = process(request);//處理請(qǐng)求
printWriter.println(response);//向客戶端寫回響應(yīng)
printWriter.flush();//記得寫回后進(jìn)行刷新緩沖區(qū)
System.out.println("響應(yīng):" + clientSocket.getInetAddress() + clientSocket.getPort() + "文本: "+ response);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
clientSocket.close();//記得要關(guān)閉
}
}
public String process(String request){
HashMap<String,String> map = new HashMap<>();
map.put("人","human");
map.put("貓","cat");
map.put("狗","dog");
return map.getOrDefault(request,"查閱失敗");
}
public static void main(String[] args) throws IOException {
TcpServer tcpServer = new TcpServer(1024);
tcpServer.start();
}
}
客戶端:
public class TcpClient {
private Socket socket = null;
private String serverIp;
private int serverPort;
public TcpClient(String serverIp,int serverPort) throws IOException {
socket = new Socket(serverIp,serverPort);//客戶端隨機(jī)分配端口號(hào)
this.serverIp = serverIp;
this.serverPort = serverPort;
}
public void start(){
try(InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
//創(chuàng)建流對(duì)象進(jìn)行寫與讀
while(true){
Scanner scanner = new Scanner(System.in);//用來寫入
PrintWriter printWriter = new PrintWriter(outputStream);//包裝output流對(duì)象
String request = scanner.next();//寫請(qǐng)求
if(request.equals("exit")){
System.out.println("結(jié)束與服務(wù)器連接");
break;
}
//把請(qǐng)求放到流對(duì)象中寫出去
printWriter.println(request);
printWriter.flush();//刷新緩沖區(qū)
Scanner responseScanner = new Scanner(inputStream);
String response = responseScanner.next();//讀服務(wù)器的響應(yīng)
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpClient tcpClient = new TcpClient("127.0.0.1",1024);
tcpClient.start();
}
}
服務(wù)器打印:
?
客戶端打印:
?文章來源:http://www.zghlxwxcb.cn/news/detail-636708.html
?
?文章來源地址http://www.zghlxwxcb.cn/news/detail-636708.html
到了這里,關(guān)于[JAVAee]網(wǎng)絡(luò)編程-套接字Socket的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!