TCP編程
首先需要了解TCP的連接過程,然後使用socket編程實現。TCP分爲客戶端和服務器端編程。下面都會介紹。
但是首先會先介紹下TCP連接的整體概念,其實新手可以先連接整個過程,然後在細緻的分析其中各個類的函數怎麼用啊,有什麼參數啊。當然可以先寫一個程序,跑起來,然後在細緻的扣含義哈。
在看下面代碼之前,建議明白什麼java的Thread類如何使用,和I/O通訊哈!
1. TCP建立鏈接的步驟
1.服務端開放一個port,被動地等待客戶端的連接。
2.客戶端向服務器端發送連接請求,需要通過該port,然後被動地等待服務器的響應, 通過I/O流與服務端通信 ,當然只有在服務器開啓的時候纔可能連接上。
3.當客戶端向服務端發出連接請求後,並被服務器正確接受並相應後,服務端創建一個sockt實例,併爲該實例創建一個新的線程,使用I/O 流與客戶端進行通訊
public void client() throws Exception {
Socket socket = new Socket(InetAddress.getLocalHost(), 8090);
// InetAddress.getLocalHost()爲客戶端請求連接的主機號,此處設置爲本地主機,服務進程的端口號是8090
// 主機號和端口號唯一確定了唯一主機上面的唯一進程。
OutputStream os = socket.getOutputStream();
// socket.getOutputStream()獲得輸出流,通過輸出流像主機發送數據。
os.write("黑貓呼叫白貓收到請回復!".getBytes());
socket.shutdownOutput();
// 關閉數據輸出,如果不關閉的話服務端並不知道數據傳輸已經結束還會一直等待。
InputStream is = socket.getInputStream();
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b)) != -1) {
String str = new String(b, 0, len);
System.out.println(str);
}
is.close();
os.close();
socket.close();
}
@Test
public void server() throws Exception {
ServerSocket ss = new ServerSocket(8090);
// 給服務端一個端口號8090使得客戶端可以連接。
Socket socket = ss.accept();
// 接受客戶端的連接
InputStream is = socket.getInputStream();
// 獲得客戶端的輸入流
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b)) != -1) {
String str = new String(b, 0, len);
System.out.println(str);
}
OutputStream os = socket.getOutputStream();
// 通過輸出流向客戶端發送數據。
os.write("黑貓這裏是白貓,我已收到你的呼叫!".getBytes());
os.close();
// socket.shutdownOutput();
is.close();
socket.close();
ss.close();
}
2. 服務器處理多客戶端請求時
在TCP Socket編程中,客戶端有多個,而服務器端只有一個。
- 由客戶端TCP向服務器端TCP發送連接請求,服務器端的ServerSocket實例則監聽來自客戶端的TCP連接請求,併爲每個請求創建新的Socket實例。因此要爲每個Socket連接開啓一個線程。(P14)
- 由於服務端在調用accept()等待客戶端的連接請求時會阻塞,直到收到客戶端發送的連接請求才會繼續往下執行代碼。
- 服務器端要同時處理ServerSocket實例和Socket實例,而客戶端只需要使用Socket實例。
- 每個Socket實例會關聯一個InputStream和OutputStream對象,通過OutputStream來發送數據,並通過從InputStream來接收數據。.
簡單來說,使用TCP方式進行網絡通訊時,需要建立專門的虛擬連接,然後進行可靠的數據傳輸,如果數據發送失敗,則客戶端會自動重發該數據 。由於TCP需要建立專用的虛擬連接以及確認傳輸是否正確,所以使用TCP方式的速度稍微慢一些,而且傳輸時產生的數據量要比UDP稍微大一些。
2.1.TCP客戶端創建步驟
- 創建一個Socket實例 指定遠程主機的ip和端口,建立一個TCP連接
- 通過I/O流與服務端通信
- 當不需要連接的時候,使用Socket類的close來關閉連接(當通信結束,可以使用Socket的close()方法關閉該客戶端連接)
具體代碼實現如下:
/**
*
* @param serverIp 服務端 IP
* @param serverPort 服務端端口
* @param msg 發生的消息
* @throws IOException
*/
public static void client(String serverIp, int serverPort, String msg) throws IOException {
String response = null;
//與端口進行連接
Socket socket = new Socket(serverIp, serverPort);
socket.setSoTimeout(5000);
PrintStream out = new PrintStream(socket.getOutputStream());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.println(msg);
try {
response = bufferedReader.readLine();
System.out.println("接收到服務端消息:"+response);
} catch (SocketTimeoutException e) {
System.out.println("連接超時,無響應");
}
if (socket != null) {
//如果構造函數建立起了連接,則關閉套接字,如果沒有建立起連接,自然不用關閉
socket.close(); //只關閉socket,其關聯的輸入輸出流也會被關閉
}
}
2.2 TCP服務端創建步驟:
創建一個ServerSocket實例並指定本地端口,用來監聽客戶端在該端口發送的TCP連接請求。當接收客戶端TCP 請求後
- 調用ServerSocket的accept()方法以獲取客戶端連接,並通過其返回值創建一個Socket實例
- 爲返回的Socket實例開啓新的線程,並使用Socket實例的I/O流與客戶端通信
- 當連接終止後,調用ServerSocket close 方法,關閉服務端
具體實現代碼:
/**
* 這個類主要是爲了用來創建Sockt 實例
*/
public class TcpServerThread implements Runnable {
private Socket socketClient = null;
String responseToClient = "你好客戶端";
public TcpServerThread(Socket socketClient) {
this.socketClient = socketClient;
}
public void run() {
try {
//獲取Socket的輸出流,用來向客戶端發送數據
PrintStream out = new PrintStream(socketClient.getOutputStream());
//獲取Socket的輸入流,用來接收從客戶端發送過來的數據
BufferedReader buf = new BufferedReader(new InputStreamReader(socketClient.getInputStream()));
boolean flag = true;
while (flag) {
//接收從客戶端發送過來的數據
String str = buf.readLine();
if (str == null || "".equals(str)) {
flag = false;
} else {
//將接收到的字符串前面加上echo,發送到對應的客戶端
System.out.println("接受到客戶端消息客戶端消息:" + str);
out.println(":" + responseToClient);
}
}
out.close();
socketClient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* @param port 服務器端口
* @throws Exception
*/
public static void tcpServer(int port)throws Exception{
ServerSocket serverSocket =new ServerSocket(port);
Socket socketClient=null;
boolean flag=true;
while (flag){
socketClient=serverSocket.accept();
System.out.println("與客戶端連接成功");
new Thread(new TcpServerThread(socketClient)).start();
}
serverSocket.close();
}
當然跑代碼的時候,肯定是先跑server,否則client的連接肯定會出錯哈,然後一定注意其中異常的拋出,還有使用完成後,需要對socket進行關閉什麼的問題。
參考:https://blog.csdn.net/u013309870/article/details/52121198
https://blog.csdn.net/github_38151745/article/details/79045713