1、什麼是ip地址
簡述:ip地址是網絡中計算機的唯一標識。
舉例:ipv4–>xx.xx.xx.xx xx是0-255之間的一個數字,4位10進制數
ipv6–>xx.xx.xx.xx.xx.xx -->6位16進制數
局域網ip(通常爲192.168.xx.xx),在局域網內,不可以重複
外網ip,在外網環境下不可以重複
【如家中連接的WiFi,移動聯通等通信公司給路由器分配一個外網ip,所有連接該wifi的設備被分配一個局域網ip,共用同一個外網ip。】
如何查看本機ip–>ipconfig
每個域名,訪問的其實都是某個服務器,服務器其實是一臺計算機。
訪問域名的時候,其實就是通過服務器的ip地址,找到服務器並獲取數據。
域名跟ip地址是一一對應的。【ping 域名 、ping ip地址】
特殊的ip地址,本地ip:
127.0.0.1 localhost
2,在Java中處理IP地址。
InetAddress表示一個IP地址對象
//可以根據主機名(計算機名(通過計算機-->屬性查看))構建InetAddress對象
//也可以根據ip地址(字符串)構建InetAddress對象
InetAddress ia = InetAddress.getByName(String name);
System.out.println(ia);
3,端口號
我們的電腦上有很多運行的程序,有的程序不需要跟外界交互(單機軟件)
有的程序需要跟外界交互,這個時候我們需要通過端口號區分我們要跟那個軟件交互。
端口號默認系統分配,也可以有程序向系統申請某一端口號,
同理,局域網ip也由路由器默認分配,但也可向其申請某一局域網ip,
若申請的端口號或ip已被佔據,則申請者會遭到拒絕。
總結:通過ip定位計算機,通過port定位哪個軟件
注意:端口號是不能重複的。端口號是一個數字(0到6萬多)。
百度百科:https://baike.baidu.com/item/%E7%AB%AF%E5%8F%A3%E5%8F%B7/10883658
可以通過 360流量防火牆-->網絡連接 查看各進程端口號
查看進程端口號的方法
4,通信協議(兩個計算機通信遵循的規則)
通信規則:
UDP–>速度快、不需要建立連接、不可靠
TCP—>速度慢、需要通過三次握手建立連接、可靠
舉例:UDP:發短信 、、TCP:打電話
網絡編程的三要素總結:
IP地址,端口號,通信協議。
5,Socket套接字
在程序中我們通過Socket進行通信,在使用Socket通信的時候
需要指定上面編程的三要素(ip,port,協議)
數據發送分成兩步
第一步是監聽(等待數據發送過來),用來接收數據。需要指定監聽的是哪個端口號
第二步是發送,需要指定發送到哪個計算機(IP地址),需要指定發送到這個計算機的哪個端口號。
Socket中分爲發送端和接收端
6,使用UDP發送數據
DatagramSocket ds = new DatagramSocket();//負責發送
//Scanner s = new Scanner(System.in); //用於循環發送數據
//while(true){ //用於循環發送數據
//String str = s.nextLine(); //用於循環發送數據
//if(str.equals("end")) break; //用於循環發送數據
//創建要發送的信息
byte[] buf = "udp傳送的信息".getBytes(); //用於循環發送數據是數據改爲str
int length = buf.length;
InetAddress address = InetAddress.getByName("localhost");//指定包裹的目的地地址
int port = 50000; //一個當前不在運行的端口號
//int port = 7874; //指定包裹的目的地端口號
//封裝數據的數據包
DatagramPacket dp = new DatagramPacket(buf,length,address,port);
ds.send(dp);//發送數據
//} //用於循環發送數據
ds.close();//釋放資源
UDP只負責將數據發出去,不管是否有人收到。上述運行成功。
Socket都是要釋放資源的,所以常常將socket.close();用finally包裹,以確保資源釋放。
(如果不釋放資源,socket會一直等待去工作)
7、使用UDP接收數據:
DatagramSocket ds = new DatagramSocket(7874); //指定接收者要監聽的本機的某一端口號
//再次啓動該語句,會由於該端口已被綁定(資源未釋放)而報異常
byte[] buf = new byte[1024];
int length = buf.length;
//while(true){ //用於持續接收數據
//創建用來裝信息的包裹基本組成
DatagramPacket dp = new DatagramPacket(buf, length);
//使用包裹 dp 去裝載接收到的信息,receive後dp中還包含其他關於發送端的信息
ds.receive(dp);
//將接收到的信息組裝成String,length直接返回data的長度
String s = new String(dp.getData(),0,dp.getLength());
System.out.println(s);
System.out.println(dp.getAddress());//獲取發送端的地址
System.out.println(dp.getPort());//獲取發送端的端口號
//發生端的端口號每次在發送信息時由系統自動分配,所以很有可能每次都不同
//} //用於持續接收數據
ds.close(); //當持續接收數據時,可不釋放資源(使其持續等待數據)
啓動時先啓動接收端,再啓動發送端。
8、多線程與UDP結合實現雙向交流:
根據6,7的內容創建UDP_sendThread和UDP_receiveThread,爲聊天1號同時開啓發送線程和接收線程,爲聊天2號也start發送線程和接收線程,即可雙向交流。
9、同理,使用TCP完成持續發送與接收數據:
//使用tcp 發送端(client,客戶端)
//與UDP的不同-->new Socket時會尋找服務器端以求建立連接
Socket send = new Socket("localhost",7878);//發送到本地7878端口
OutputStream out = send.getOutputStream();
Scanner s = new Scanner(System.in);
while(true) {
String str = s.nextLine();
if(str.equals("end")) break;
out.write(str.getBytes());
}
send.close();
接收端:
//接收端(server,服務器端)
ServerSocket ss = new ServerSocket(7878);//監聽7878端口
//程序在這裏停止,等待Socket建立連接,發送端new Socket("localhost",7878)時建立連接
Socket receive = ss.accept();
InputStream is = receive.getInputStream();
byte[] bt = new byte[1024];
int length =-1;
/*這個輸入流要麼[管道中有數據-->接收數據],要麼處於[管道中沒數據-->等待狀態],
在之前學習的IO流的read方法是不會暫停(等待)的*/
/*當客戶端close-->客戶端不再向管道中寫數據--->流結束-->輸入流的read會返回-1
與UDP的不同--->服務器端知道客戶端的狀態*/
while((length = is.read(bt))!=-1) {
System.out.println(new String(bt,0,length));
}
//在得到客戶端關閉後,將服務器端進行關閉
receive.close();
ss.close();
10、使用TCP實現雙向交流
使用【多線程】以及【服務器端Socket與客戶端Socket都可獲得輸入流及輸出流】來使服務器端與客戶端 都 同時開啓接收和進行輸入 即可。(注意仍要先打開服務器端,再打開客戶端!)
11、使用TCP實現多客戶端向服務器端發信息
服務器端持續等待新的客戶端的接入,並在每次與客戶端接入後在服務器端創建一個新的對應的Socket,使通道連通【該通道建立於一個新的線程,用read來持續等待接收數據】。
12、枚舉類型【見名知意,增加代碼可讀性】
手動實現枚舉類型:
class SeasonDemo{
static final int Spring = 0;
static final int SUMMER = 1;
static final int FALL = 2;
static final int WINTER = 3;
}
-----------------------------------
通過Enum實現枚舉類型:
enum SeasonDemo {
//枚舉類型,默認數值從0開始+1遞增
Spring,SUMMER,FALL,WINTER
}
對枚舉類型的應用:
//枚舉類型的賦值
SeasonDemo sd1 = SeasonDemo.Spring;
SeasonDemo sd2 = SeasonDemo.WINTER;
System.out.println(sd1.name());//返回定義的name:Spring
System.out.println(sd2.ordinal());//返回對應的int值:3
System.out.println(sd1 == SeasonDemo.Spring);//true,Object數據的比較
//遍歷所有的SeasonDemo值
for(SeasonDemo s:SeasonDemo.values())
System.out.println(s);
//返回Spring,SUMMER,FALL,WINTER