Socket程式設計學習筆記(一)
一、什麼是Socket程式設計:
socket是一種最簡單的客戶機/伺服器通訊模式。即客戶程序向伺服器程序發出某種服務請求,伺服器響應該請求。如圖所示,同常,一個伺服器程序會同時為多個客戶程序服務,圖中的伺服器程序B1同時為客戶程序A1、A2和B2提供服務。
Socket也稱為“套接字”,用於描述IP地址和埠,是一個通訊鏈的控制代碼。應用程式常通過“套接字”向網路發出請求或應答網路請求。它具有以下幾個特點:
①Socket是連線執行在網路上的2個程式間的雙向通訊的端點。
②網路通訊實際上Socket的通訊。
③通訊的兩端都有Socket,資料在兩個Socket間通過IO進行傳輸。
二、使用Socket進行網路通訊的過程
①伺服器程式將一個套接字繫結到一個特定的埠,並通過此套接字等待和監聽客戶端的連線請求。
②客戶程式根據伺服器程式所在的主機名和埠號發出連線請求。
③如果一切正常,伺服器接收連線請求,並獲得一個新的繫結到不同埠地址的套接字。
④客戶和伺服器通過連續讀寫套接字進行通訊。
一般說來Socket程式設計又分為基於TCP的Socket程式設計和基於UDP的Socket程式設計
三、建立TCP服務端/客戶端:
建立TCP服務端
①建立一個ServerSocket物件
②呼叫accept()方法接收客戶端請求
③從Socket中獲取IO流
④對I/O流進行讀寫操作,完成與客戶端的互動
⑤關閉I/O流和Socket
建立TCP客戶端
①建立一個Socket物件
②從Socket中獲取IO流
③對IO流進行讀寫操作,完成與服務端的互動
④關閉IO流和Socket
強調:客戶端和服務端進行資料傳輸時,客戶端的輸入流對應服務端的輸出流,客戶端的輸出流對應服務端的輸入流。
//服務端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketTest1 {
public static void main(String[] args) throws IOException{
ServerSocket serverSocket = new ServerSocket(8080);//將服務端與8080埠繫結
Socket socket = serverSocket.accept();//監聽8080埠
System.out.println("ip:"+socket.getInetAddress());
System.out.println("port:"+socket.getPort());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
String str = null;
while((str = reader.readLine()) != null){
if(str.equals("quit"))//如果接受到的資訊是"quit",就退出
break;
System.out.println("get:"+str);
writer.println("server get it");
}
serverSocket.close();
socket.close();
reader.close();
writer.close();
}
}
這種寫法的問題是如果同時有多個客戶端訪問,就會出現排隊現象,非常影響效率。所以,我們可以使用執行緒的知識,每次有客戶連線,就線上程中處理該請求。修改後的服務端程式碼如下
//客戶端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketTest {
public static void main(String[] args)throws IOException {
ServerSocket serverSocket = new ServerSocket(8080,2);
int flag = 0;
System.out.println("server is ready");
while(true){
Socket socket = serverSocket.accept();
System.out.println("Connect:"+socket.getLocalAddress());
System.err.println("client id:"+(++flag));
System.out.println("Client Port:"+socket.getPort());
handleConnection(socket);//線上程中進行處理
}
//serverSocket.close();
}
private static void handleConnection(Socket socket)throws IOException{
new Thread(new Runnable() {
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
String str = null;
while((str = reader.readLine()) != null){
if(str.equals("quit"))
break;
System.out.println("Receive:"+str);
writer.println("Server received");
}
socket.close();
reader.close();
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
四、基於UDP的Socket程式設計
1.建立傳送端:
①建立DatagramSocket物件,該端點建立時,系統會隨機分配一個埠,如果不想隨機配置,可以手動指定。
②將資料進行Packet包(DatagramPacket)封裝,必須要指定目的地址和埠
③通過Socket服務的Send方法將該包發出
④將Socket關閉
2.建立接收端:
①建立DatagramSocket物件,要監聽一個埠
②通過Socket的receive方法將資料存入資料包中
③通過資料包DatagramPacket的方法getData()、getAddress()、getPort()獲取包中的指定資訊。
④將socket關閉
//接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SocketReceiveTest {
public static void main(String[] args) throws IOException{
DatagramSocket socket = new DatagramSocket(8080);//監聽8080埠
byte[] bytes = new byte[100];
DatagramPacket packet1 = new DatagramPacket(bytes, bytes.length);
socket.receive(packet1);
System.out.println(new String(packet1.getData()));
String str = "hello smart";
DatagramPacket packet2 = new DatagramPacket(str.getBytes(), str.length(),
InetAddress.getByName("localhost"), packet1.getPort());
socket.send(packet2);//接收到訊息後返回內容,埠就是傳送端的傳送埠。
socket.close();
}
}
//傳送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SocketSendTest {
public static void main(String[] args) throws IOException{
DatagramSocket socket = new DatagramSocket();//這裡可以不指定本方的傳送埠
String str = "hello world";
DatagramPacket packetSend = new DatagramPacket(str.getBytes(), str.length(),
InetAddress.getByName("localhost"), 8080);//指定傳送到localhost的8080埠
System.out.println("send port:"+packetSend.getPort());
socket.send(packetSend);
byte[] bytes = new byte[100];
DatagramPacket packetReceive = new DatagramPacket(bytes, bytes.length);
socket.receive(packetReceive);
System.out.println(new String(packetReceive.getData()));
socket.close();
}
}