基本任務
- 掌握常用的網絡命令
- 對網絡通信有基本理解
- 能編寫簡單的網絡通信服務器和客戶端程序
- 能自定義通信協議,實現複雜的通信機制
- 完成一個完整的網絡聊天系統
作爲學習計算機網絡,想要開發通信項目的人,我想你應該知道每臺機器有一個IP地址,相當於人的名字方便識別,在計算機中如果機器A想和B通信時,不僅需要知道B的IP地址,還需要知道B在哪個端口等待,就好比A如果想到B家裏做客,它不知道B的家在哪裏,B要去路口接A,這樣A只知道B的名字是不夠的,需要知道B在哪接他,這裏的路口就相當於端口。每臺機器有0-65535個端口,每個端口可供一個應用,通常我們要避免使用“知名端口”(0-1024),他是規定給一些常用應用使用的,例如打開一個網頁,連接的是80端口,DNS(域名解析)使用的是53端口。
一. 常用的網絡命令
1.現在我們利用命令ping netjava.cn查看網絡是否通暢(ping命令使用ICMP報文工作在TCP層,它只能查詢網絡是否順暢並不能證明主機是否開放某個端口)
我們可以看到網絡連接通暢,測試的主機連接了netjava.cn這個網絡,開放默認端口爲80,當然我們也可以查詢其他,如:
2.利用telnet+ip地址+端口號查看端口是否連接。按下回車,如果出現如下窗口則表示連接成功,這時,輸入字符觀察現象
我們可以看到,服務器與web使用的是HTTP協議,我們輸入的字符不滿足協議要求無法解析,所以失去了與主機的連接
3.利用命令netstat命令查看測試主機與哪些網絡通信
從上圖可以看到測試主機與其他服務器建立了許多的TCP和UDP連接
二、簡單的服務器創建
首先來了解一下基本的網絡通信:我們用打電話來舉例說明。我們要實現一個電話的連接首先需要一臺手機並且需要一個電話卡,我們將手機進入開機等待模式,等待別人cll你,這裏手機和卡號就相當於服務器對象和設定的端口號,當電話連接時,我們會接到對方的語音流,自己也會說話迴應,這裏的語音流就是輸入輸出流
所以實現一個簡單的服務器將客戶機發來的字符串顯示出來並送回到客戶機分爲如下幾步:
1.在指定端口創建服務器對象Socket
2.讓服務器進入等待狀態,等待客戶機的連接
3,從Socket對象調用方法獲取輸入輸出流
package com.lm.helloServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HelloServer {
public void setUpServer(int port) {
try {
// 1.在指定端口創建服務器對象
ServerSocket server = new ServerSocket(port);
System.out.println("服務器創建成功!"+port);
// 2.等待客戶機連接
Socket client = server.accept();
// 3.從連接對象獲取輸入輸出流
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
String s = "歡迎歡迎";
byte[] data = s.getBytes();
out.write(data);//用輸出對象發送數據
out.flush();//強制輸出
client.close();//關閉連接
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
HelloServer hs = new HelloServer();
hs.setUpServer(9090);
}
}
運行:
查看主機:
在這個程序中,我們只是創建了服務器,利用的是telnet命令使用的客戶機,可以看出客戶端連接了我們創建的服務器並收到了消息。不論我們發送的是文本還是圖片或是字符串,在傳輸過程中都是以字節的方式進行的,一個字節是由一組二進制組成的。
如何讓服務器讀取客戶機發送的信息,顯然是可以的。我們讓服務器循環接收讀取客戶機信息直到收到bye接收通信。
/**
* 單線程服務器通信
*/
package com.lm.helloServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HelloServer {
public void setUpServer(int port) {
try {
// 1.在指定端口創建服務器對象
ServerSocket server = new ServerSocket(port);
System.out.println("服務器創建成功!"+port);
// 2.等待客戶機連接
Socket client = server.accept();
System.out.println("client's socketAddress"+client.getRemoteSocketAddress());
processChat(client);//"調用處理連接對象的方法處理連接對象"
// 3.從連接對象獲取輸入輸出流
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
String s = "歡迎歡迎";
byte[] data = s.getBytes();
out.write(data);//用輸出對象發送數據
out.flush();//強制輸出
client.close();//關閉連接
} catch (IOException e) {
e.printStackTrace();
}
}
public void processChat(Socket client) {
try {
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
String s = "歡迎來到服務器!\r\n";
byte[] data = s.getBytes();
out.write(data);
out.flush();
String inputS = readString(in);
while(!inputS.equals("bye")) {
System.out.println("客戶機說:"+inputS);
s="服務器收到:"+inputS +"\r\n";
data = s.getBytes();
out.write(data);
out.flush();
inputS = readString(in);//讀取客戶機下一次輸入
}
s = "歡迎下次連接!\r\n";
data = s.getBytes();
out.write(data);
out.flush();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public String readString(InputStream in) {
StringBuffer sb = new StringBuffer();
char c = 0;
while(c!=13) {
int i;
try {
i = in.read();//讀取客戶機發來的字節
c=(char)i;//將輸入的字節轉化爲Char
sb.append(c);
} catch (IOException e) {
e.printStackTrace();
}
}
String inputS = sb.toString().trim();
return inputS;
}
public static void main(String[] args) {
HelloServer hs = new HelloServer();
hs.setUpServer(9090);
}
}
當我們打另一個客戶機的時候發現,服務器只能與一個客戶機通信,如何改善???這就需要使用操作系統裏的多線程了!
三、多線程服務器的創建
單線程服務器通信圖:
多線程服務器通信圖:
如果還有對多線程不太瞭解的朋友可以去查閱一下相關資料,這裏就不多做介紹了!
我們每與一個客戶機連接就創建一個線程來處理,所以要將處理客戶端連接對象封裝成線程類
詳細代碼參考:Chats.rar文件(https://github.com/zhenglimei/chatServer)