網絡程式設計
概述
現在的網絡程式設計基本上都是基于請求/響應方式的,也就是一個裝置發送請求資料給另外一個,然後接收另一個裝置的回報。
在網絡程式設計中,發起連接配接程式,也就是發送第一次請求的程式,被稱作用戶端(Client),等待其他程式連接配接的程式被稱作伺服器(Server)。
網絡通信協定
連接配接和通信的規則被稱為網絡通信協定,它對資料的傳輸格式、傳輸速率、傳輸步驟等做了統一規定,通信雙方必須同時遵守才能完成資料交換。
IP位址和端口号
IP位址:InetAddress
網絡中裝置的辨別
不易記憶,可用主機名
本地回環位址:127.0.0.1 主機名:localhost
端口号
用于辨別程序的邏輯位址,不同程序的辨別。邏輯位址,區分不同的服務
有效端口:0~65535,其中0~1024系統使用或保留端口。
備注:不是所謂的實體端口!
InetAddress
InetAddress:構造方法私有,不能直接建立對象。
InetAddress getByName(String host): 在給定主機名的情況下确定主機的ip位址。
InetAddress getLocalHost():傳回本地主機。
InetAddress[] getAllByName(String host);
ip.getHostAddress()
ip.getHostName()
Eg:
package com.li.net;
import java.net.InetAddress;
public class Demo1 {
public static void main(String[] args) throws Exception {
InetAddress i = InetAddress.getLocalHost();
System.out.println(i);
i = InetAddress.getByName("www.baidu.com");
System.out.println(i);
System.out.println(i.getHostAddress());
System.out.println(i.getHostName());
}
}
輸出:
XP-201706252326/10.6.147.2
www.baidu.com/61.135.169.105
61.135.169.105
www.baidu.com
UDP與TCP協定
TCP/IP協定(Transmission Control Protocal/Internet Protoal傳輸控制協定/英特網互聯協定),它是一個包括TCP協定和IP協定。
UDP(User Datagram Protocol)使用者資料報協定 ICMP(Internet Control Message Protocol)協定和其它一些協定的協定組
差別
類似于圖像、聲音等對可靠性要求沒有那麼高的業務可以用UDP,他們不需要準确存儲對準确性無要求但要求速度快。
類似于文本、程式、檔案等要求可靠的資料最好就用TCP,但會犧牲一些速度。
對系統資源的要求:TCP較多,UDP少。
程式結構:UDP程式結構較簡單,TCP複雜。
流模式與資料報模式:TCP保證資料正确性,UDP可能丢包; TCP保證資料順序,UDP不保證
用途
TCP是面向連接配接的,有比較高的可靠性,一些要求比較高的服務一般使用這個協定,如FTP、Telnet、SMTP、HTTP、POP3等,而 UDP是面向無連接配接的,使用這個協定的常見服務有DNS、SNMP、QQ等。
UDP是一種面向無連接配接的通信協定,該協定使得資料傳輸的速度得到大幅度的提高。視訊聊天語音聊天基本都是用UDP協定。
總結
UDP:
1、将資料源和目的地封裝到資料包中,不需要建立連接配接
2、每個資料包的大小限制在64k以内
3、因無連接配接,是不可靠協定
4、不需要建立連接配接,速度快
例子:聊天、對講機就是UDP的,面向無連接配接(不管在不在,知不知道,隻管發送,求速度),丢資料也不管。速度快。資料被分成包
TCP:
1、建立連接配接,形成傳輸資料的通道
2、在連接配接中進行大量資料的傳輸
3、通過三次握手完成連接配接、是可靠協定
4、必須建立連接配接,效率會稍低
例子:電話通話,必須連接配接,對方同意才可以發送資料(不然就等待),不能丢失資料。
UDP通信
DatagramPacket
DatagramPacket類的執行個體對象就相當于一個集裝箱,用于封裝UDP通信中發送或者接收的資料。
通過這個對象中的方法,就可以擷取到資料包中的各種資訊。
byte[] getData()
int getLength()
int getPort()
setData(byte[]
setPort()
DatagramSocket
DatagramSocket類的作用就類似于碼頭,使用這個類的執行個體對象就可以發送和接收DatagramPacket資料包,封裝了udp傳輸協定的socket對象。
具備發送和接受功能,在進行udp傳輸時,需要明确一個是發送端,一個是接收端。
receive(DatagramPacket)
send(DatagramPacket)
UDP網絡程式設計
udp的發送端
①:建立udp的socket服務,建立對象時如果沒有明确端口,系統會自動配置設定一個未被使用的端口。
②:明确要發送的具體資料。
③:将資料封裝成了資料包。
④:用socket服務的send方法将資料包發送出去。
⑤:關閉資源。
發送端(用戶端)
import java.net.*;
class UdpSend {
public static void main(String[] args)throws Exception {
// 1,建立udp的socket服務。
DatagramSocket ds = new DatagramSocket(8888);//指定發送端口,這個可以不指定,系統會随機配置設定。
// 2,明确要發送的具體資料。
String text = "udp傳輸示範 哥們來了";
byte[] buf = text.getBytes();
// 3,将資料封裝成了資料包。
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("10.1.31.127"),10000);
// 4,用socket服務的send方法将資料包發送出去。
ds.send(dp);
// 5,關閉資源。
ds.close();
}
}
udp的接收端
①:建立udp的socket服務,必須要明确一個端口,作用在于,隻有發送到這個端口的資料才是這個接收端可以處理的資料。
②:定義資料包,用于存儲接收到資料。
③:通過socket服務的接收方法将收到的資料存儲到資料包中。
④:通過資料包的方法擷取資料包中的具體資料内容,比如ip、端口、資料等等。
⑤:關閉資源。
接收端(伺服器端)
import java.net.*;
class UdpRece {
public static void main(String[] args) throws Exception {
// 1,建立udp的socket服務。
DatagramSocket ds = new DatagramSocket(10000); //必須指定,并且和上面的端口号一樣!
// 2,定義資料包,用于存儲接收到資料。先定義位元組數組,資料包會把資料存儲到位元組數組中。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
// 3,通過socket服務的接收方法将收到的資料存儲到資料包中。
ds.receive(dp); //該方法是阻塞式方法。
// 4,通過資料包的方法擷取資料包中的具體資料内容,比如ip,端口,資料等等。
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength()); //将位元組數組中的有效部分轉成字元串。
System.out.println(ip+":"+port+"--"+text);
// 5,關閉資源。
ds.close();
}
}
UDP案例--聊天程式
通過鍵盤錄入擷取要發送的資訊。
将發送和接收分别封裝到兩個線程中。
package com.li.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//用戶端,發送端
class Send implements Runnable {
private DatagramSocket ds;
public Send(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//資料源是鍵盤錄入
String line;
while ((line = br.readLine()) != null) {
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length,InetAddress.getByName("localhost"), 10225);
ds.send(dp);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 伺服器端,接收端
class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
try {
while (true) {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, 0, buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(), 0, dp.getLength());
System.out.println(ip + " " + data);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Demo6 {
public static void main(String[] args) throws Exception {
DatagramSocket sendDs = new DatagramSocket();
DatagramSocket receDs = new DatagramSocket(10225);
new Thread(new Send(sendDs)).start();
new Thread(new Rece(receDs)).start();
}
}
輸出:
你好
127.0.0.1 你好
你好
127.0.0.1 你好
TCP通信
TCP通信是嚴格區分用戶端與伺服器端的,在通信時,必須先由用戶端去連接配接伺服器端才能實作通信,伺服器端不可以主動連接配接用戶端,并且伺服器端程式需要事先啟動,等待用戶端的連接配接。
在JDK中提供了兩個類用于實作TCP程式,一個是ServerSocket類,用于表示伺服器端,一個是Socket類,用于表示用戶端。
ServerSocket
ServerSocket(端口) - 構造器
setSoTimeout(int)
Socket accept()
close()
Socket
Socket就是為網絡服務提供的一種機制。
Socket(IP位址, 端口) - 構造器
getInputStream()
getOutputStream()
getChannel()
close()
TCP案例--檔案上傳
題目:上傳檔案,多用戶端上傳,并且保證不會因為檔案的名稱而重複!
用戶端:
package com.li.net;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Demo22 {
public static void main(String[] args) throws Exception {
Socket s = new Socket("localhost", 12036);
BufferedReader br = new BufferedReader(new FileReader("E:/你好.txt"));
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
BufferedReader br2 = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line;
while((line = br.readLine()) != null) {
pw.println(line);
}
s.shutdownOutput();
String str = br2.readLine();
System.out.println(str);
s.close();
}
}
伺服器端:
package com.li.net;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
class MyUpdate implements Runnable {
private Socket s;
public MyUpdate(Socket s) {
super();
this.s = s;
}
@Override
public void run() {
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".........connected!");
int count = 0;
try {
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
File file = new File("E:/");
File f = new File(file,"你好"+count+".txt");
while(f.exists()) { //如果寫成if,就不可以!
f = new File(file,"你好"+(++count)+".txt");
}
PrintWriter pw = new PrintWriter(new FileWriter(f),true);
PrintWriter pw2 = new PrintWriter(s.getOutputStream(),true);
String line;
while((line = br.readLine()) != null) {
pw.println(line);
}
pw2.println("恭喜您,上傳成功!");
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Demo23 {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(12036);
while(true) {
Socket s = ss.accept();
new Thread(new MyUpdate(s)).start();
}
}
}