[Java 網路程式設計 03] TCP/UDP 基於socket的實現 2021.11.11
1. 網路程式設計要素:
- 如何定位到網路上的一臺或多臺主機 IP地址和埠號
- 找到後如何通訊 TCP/UDP
- JAVA,萬物皆物件,去找相應的類
2. IP地址
ip地址的包: InetAddress
-
唯一定位一臺網路上的計算機
-
本機localhost地址: 127.0.0.1
-
ip地址分類:
- IPV4/IPV6
- IPV4
- IPV6
- 公網-私網
- ABCD類地址
- 192.168.xxx.xxx, 給組織內部使用
- IPV4/IPV6
3. InetAddress包
沒有建構函式,只能通過靜態方法返回物件
直接檢視java文件,獲取本機地址/域名地址等,
得到的返回值為一個物件,獲取他們的資訊
InetAddress localhost = InetAddress.getLocalHost();
localhost.getAddress();
localhost.getcanonicalHostName();
localhost.getHostAddress();
localhost.getHostName();
4. 埠
埠表示計算機上一個程式的程序
-
不同的程序有不同的埠號,用來區分軟體
-
埠被規定為0--65535
-
TCP/UDP個有65536個,TCP和UDP都有埠80,並且可以同時使用
-
埠分類:
-
公有埠:0--1023
- HTTP 80, HTTPS 443
- FTP 21, Telent 23
-
程式註冊埠(分配給使用者或程式): 1024--49151
- Tomcat: 8080
- MySql: 3306
- Oracle: 1521
-
動態/私有: 49152-65535(不常用)
-
常用的Dos命令:
netstat -ano # 檢視所有的埠
netstat -ano | findstr 80 #檢視指定的埠(|為過濾的意思,把後面的條件帶入前面去查詢)
tasklist | findstr 8696 # 檢視指定埠的程序(去工作管理員檢視到PID之後去找)
本機host檔案目錄: C:\Windows\System32\drivers\etc
InetSocketAddress(ip, port)//有構造方法,可以被呼叫 //同樣的方法返回埠號,ip地址,hostname等
5. 通訊協議
TCP:使用者傳輸協議,UDP:使用者資料包協議,IP:網路互聯協議
-
TCP
-
連線,穩定
-
三次握手,四次揮手
連線--斷開流程
A:瞅啥 B:瞅你咋地 A:幹一場 A:我要斷開了 B: 我知道你要斷開了 B: 你真的斷開了嗎 A:我真的斷開了
-
客戶端,服務端
-
傳輸完成,釋放連線,效率低
-
-
UDP
- 不連線,不穩定
- 客戶端,服務端,沒有明確的界限
- 不管有沒有準備好,都發過來
6. TCP通訊
- 客戶端:
- 連線伺服器 Socket
- 傳送訊息(IO流)
- 服務端:
- 建立服務埠ServerSocket
- 等待使用者連線 accept
- 接受使用者訊息(IO流)
//客戶端程式碼:
public class TcpClientDemo01 {
public static void main(String[] args){
InetAddress ServerIP = null;
Socket socket = null;
OutputStream os = null;
try {
//1. 要知道服務其的地址和埠號
ServerIP = InetAddress.getByName("127.0.0.1");
int port = 9999;
//2. 建立socket通訊
socket = new Socket(ServerIP, port);
//3. 連線完成後,傳送IO流
os = socket.getOutputStream();
os.write("whats the fuck".getBytes());
} catch (Exception e) {
e.printStackTrace();
}
finally {
if(os!= null) {
try {
os.close();
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
//伺服器端程式碼:
public class TcpServerDemo01 {
public static void main(String[] args) {
ServerSocket serverSocket =null;
Socket socket =null;
InputStream is =null;
ByteArrayOutputStream baos =null;
try {
//1. 建立客戶端要被連線的IP地址和埠號
serverSocket = new ServerSocket(9999);
//2. 等待客戶端連線
socket = serverSocket.accept();
//3. 讀取IO流
is = socket.getInputStream();
//輸入流可能亂碼,為防止,套接一個輸出流,C輸出,S輸入,套接一個頭輸出
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];//定義緩衝區
int len;
while((len = is.read(buffer))!=-1){//獲得buffer的每一個位置
baos.write(buffer, 0, len-1);
}
System.out.println(baos.toString());
//finally裡面關閉流等管道操作
} catch (IOException e) {
e.printStackTrace();
}finally {
if(baos != null) {
try {
baos.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
7. TCP檔案傳輸(確定傳輸完成時間)管道判斷
client:! 檔案在module下面,不在src目錄下面, 在src上一級
public class TcpClient {
public static void main(String[] args) throws Exception{
//1.建立socket通訊
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
//2. 讀入檔案,寫出流
File test = new File("test.jpg");
FileInputStream fis = new FileInputStream(test);
OutputStream fos = socket.getOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer, 0, len);
}
//3. socket通知伺服器流寫出結束
//通知伺服器傳送完成是由socket進行的,不是output流
socket.shutdownOutput();
//4. 建立輸入流,等待伺服器接收結束的通知
InputStream fDidDown = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while((len2 = fDidDown.read(buffer2))!= -1){
baos.write(buffer2, 0, len2);
}
System.out.println(baos.toString());
//關閉
baos.close();
fDidDown.close();
fos.close();
fis.close();
socket.close();
}
}
server:
public class TcpServer {
public static void main(String[] args) throws Exception {
//1. 建立服務並監聽
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
//2. 接收輸入並儲存檔案
InputStream is = socket.getInputStream();
OutputStream fout = new FileOutputStream(new File("receive.jpg"));
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fout.write(buffer, 0, len);
}
//3. 輸出流通知客戶端輸出完畢
OutputStream fHaveDone = socket.getOutputStream();
fHaveDone.write("檔案接收完成".getBytes());
//關閉
fHaveDone.close();
fout.close();
is.close();
socket.close();
serverSocket.close();
}
}
8. Tomcat
tomcat檔案目錄啟動start, 配置檔案修改,專案位置:webapps/root
9. UDP通訊
用到兩個包: DatagramSocket, DatagramPacket
重點:datagramPacket的構造方法,傳送端五個引數,接收端3個引數
sender:
- 建立socket連線 2. 新建一個數據包 3. 傳送 4. 關閉socket通訊
public class UdpSender {
public static void main(String[] args) throws IOException {
//1. 建立一個socket
DatagramSocket socket = new DatagramSocket();
//2. 新建一個包
String Msg = "a UDP packet";
byte[] Msg_Byte = Msg.getBytes();
DatagramPacket dataPacket = new DatagramPacket(Msg_Byte, 0,
Msg_Byte.length, InetAddress.getByName("127.0.0.1"), 9999);
//3. 傳送包
socket.send(dataPacket);
//4. 關閉socket
socket.close();
}
}
receiver:
- 提供通訊 2. 接收資料 3. 操作資料 4. 關閉socket通訊
public class UdpReceiver {
public static void main(String[] args) throws IOException {
//1. 提供服務
DatagramSocket socket = new DatagramSocket(9999);
//2. 接收資料包
byte[] buffer = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
socket.receive(datagramPacket);
//3. 操作資料
System.out.println(new String(datagramPacket.getData(), 0, datagramPacket.getData().length));
//4. 關閉socket
socket.close();
}
}
10. UDP 聊天實現
低版本: sender可以一直從鍵盤讀取,receiver 可以一直接收
Sender:
public class Sender {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(9999);//本地埠號
while(true){
//InputStreamReader引數是鍵盤輸入,把InputStream給bufferedReader, 然後讀取下一行
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String Msg = bufferedReader.readLine();//獲取下一個輸入
DatagramPacket datagramPacket = new DatagramPacket(//新建包,要連線的Socket在這裡打包
Msg.getBytes(), 0, Msg.getBytes().length, new InetSocketAddress("localhost", 9998));
socket.send(datagramPacket);//傳送
if (Msg.equals("bye")){
break;
}
}
socket.close();
System.out.println("聊天結束");
}
}
Receiver:
public class Receiver {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(9998);//本地埠號
while(true){
//接收輸入
byte[] buffer = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
socket.receive(datagramPacket);
//把輸入轉換為String
String Msg_output = new String(datagramPacket.getData(), 0, datagramPacket.getData().length);
System.out.println(Msg_output);
if (Msg_output.equals("bye")){//有問題不能實現
break;
}
}
System.out.println("聊天結束");
socket.close();
}
}
11. 多執行緒UDP諮詢視窗
Java UPD的多執行緒線上諮詢聊天視窗的實現
先寫功能塊,寫一個傳送執行緒和接收執行緒,( 就是重構,抽取特徵,構建成類)
然後建立兩個使用者,實現這兩個方法
Sender
public class Sender implements Runnable{
private int fromPort;
private String toIP;
private int toPort;
DatagramSocket socket;
BufferedReader bufferedReader;
public Sender(int fromPort, String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try{
socket = new DatagramSocket(fromPort);
bufferedReader = new BufferedReader(new InputStreamReader(System.in));
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
String Msg = null;
try {
Msg = bufferedReader.readLine();
DatagramPacket datagramPacket = new DatagramPacket(
Msg.getBytes(), 0, Msg.getBytes().length, new InetSocketAddress(toIP, toPort));
socket.send(datagramPacket);
if (Msg.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
System.out.println("聊天結束");
}
}
Receiver
public class Receiver implements Runnable {
DatagramSocket socket;
private int myPort;
public Receiver(int myPort) {
this.myPort = myPort;
try{
socket = new DatagramSocket(myPort);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
byte[] buffer = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
try {
socket.receive(datagramPacket);
String Msg_output = new String(datagramPacket.getData(), 0, datagramPacket.getData().length);
int FromPort = datagramPacket.getPort();
System.out.println("["+FromPort + "]" + Msg_output);
if (Msg_output.equals("bye")){//又問題不能實現
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("聊天結束");
socket.close();
}
}
User1
public class User1 {
public static void main(String[] args) {
new Thread(new Sender(9991,"localhost",10002)).start();
new Thread(new Receiver(10001)).start();
}
}
User2
public class User2 {
public static void main(String[] args) {
new Thread(new Sender(9992,"localhost",10001)).start();
new Thread(new Receiver(10002)).start();
}
}
12. URL
統一資源定位符:
5部分組成:
#有五部分組成:
協議//:ip地址:埠/專案名稱/資源
URL rul = new URL("http://localhost:8080/helloworld/index.jsp?username=kk&password=123");
//很多方法
url.getHost();getProtocol();...
通過URL類下載東西
public class UrlDownload {
public static void main(String[] args) throws IOException {
//下載地址
URL url = new URL("http:loaclhost:8080/projectname/file.txt");
//連線到這個資源
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//獲得輸入流,寫到本地
InputStream inputStream = httpURLConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("download.txt");
byte[] buffer = new byte[1024];
int len;
while((len = inputStream.read(buffer))!=-1){
fos.write(buffer, 0, len);
}
fos.close();
inputStream.close();
httpURLConnection.disconnect();//斷開連線
}
}
(審查元素,去下載收費歌曲)