十六、網絡編程
1、系統結構圖(xmind)
2、tips
——1.網絡模型:OSI參考模型和TCP/IP參考模型
一般來說開發處於傳輸層和網際層,應用層爲:FTP和HTTP協議等,傳輸層爲:UDP和TCP等,網際層爲:IP。
通常用戶操作的是應用層,而編程人員需要做的是傳輸層和網際層,用戶在應用層操作的數據,經過逐層封包,最後到物理層發送到另一個模型中,再進行逐層解包,圖示:
——2.網絡通信三要素
1、IP地址
1)它是網絡中的設備標識
2)不易記憶,可用主機名錶示,兩者存在映射關係
3)本機迴環地址:127.0.0.1,主機名爲:localhost。
IP地址:java中對應的是InetAddress類,存在於java.net包中。
InetAddress類:
1.無構造函數,可通過getLocalHost()方法獲取InetAddress對象,此方法是靜態的,返回本類對象。
InetAddress i = InetAddress.getLocalHost();
2.方法:
1)static InetAddress getByName(String host):獲取指定主機的IP和主機名。(最好用ip地址去獲取,主機名需要解析)
2)static InetAddress[] getAllByName(String host):在給定主機名的情況下,根據系統上配置的名稱服務返回IP地址所組成的數組。返回對象不唯一時,用此方法。
3)String getHostAddress():返回IP地址字符串文本形式,以IP地址爲主。
4)String getHostName():返回IP地址主機名。
3.如何獲取任意一臺主機的IP地址對象:
1)功能:返回InetAddress對象
2)對於任意主機,需要指定傳入主機名的參數
慄:
import java.net.*;
class IPDemo
{
public static void main(String[] args)throws Exception
{
//獲取本類對象
InetAddress ia=InetAddress.getLocalHost();
//ip
String address=ia.getHostAddress();
//主機名
String name=ia.getHostName();
System.out.println("IP="+address+"\tname="+name);
//獲取指定主機的ip信息
InetAddress i=InetAddress.getByName("192.168.1.175");
String add=i.getHostAddress();
String na=i.getHostName();
System.out.println("addIP="+add+"\tiname="+na);
//獲取指定主機名的ip信息
InetAddress[] baidu=InetAddress.getAllByName("www.baidu.com");
for (InetAddress b :baidu)
{
String baddress=b.getHostAddress();
String bname=b.getHostName();
System.out.println("baiduIP="+baddress+"\tbaiduname="+bname);
}
}
}
運行結果:
2、端口號:
1) 用於標識進程的邏輯地址,不用進程的標識。
2) 有效端口:0 ~65535,系統使用或保留的端口是:0~ 1024。
3、傳輸協議:
即通信規則,包含TCP和UDP協議
——3.TCP
1、是面向連接的,必須連接成功才能傳輸數據,應用於下載等程序上
2、協議特點:
1)面向連接,在建立連接後,形成傳輸數據的通道
2)在連接中進行大數據量的傳輸
3)通過三次握手完成連接,是可靠的協議
4)必須建立連接,效率稍慢
三次握手:第一次本方發送請求,第二次對方確認連接,第三次本方再次確認連接成功。
3、通信的步驟:
1)找到IP地址
2)數據要發送到對象指定應用程序,爲標識這些應用程序,所以給這些網絡應用程序都用數字標識,爲方便稱呼這個數字,叫做端口,即邏輯端口。
3)定義通信規則,稱之爲協議。國際組織定義了通用協議,即TCP/IP。
4.TCP客戶端
客戶端需要明確服務器的ip地址以及端口,這樣纔可以去試着建立連接,如果連接失敗,會出現異常。
連接成功,說明客戶端與服務端建立了通道,那麼通過IO流就可以進行數據的傳輸,而Socket對象已經提供了輸入流和輸出流對象,通過getInputStream(),getOutputStream()獲取即可。與服務端通訊結束後,關閉Socket。
視頻例程:
import java.net.Socket;
import java.io.OutputStream;
import java.io.IOException;
import java.net.UnknownHostException;
import java.io.InputStream;
public class ClientDemo
{
public static void main(String[] args) throws UnknownHostException,IOException
{
Socket socket = new Socket("192.168.1.100",10002);
OutputStream out = socket.getOutputStream();
out.write("tcp演示:哥們又來了!".getBytes());
//讀取客戶端返回的數據,使用Socket讀取流。
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
socket.close();
}
}
5.TCP服務端
服務端需要明確它要處理的數據是從哪個端口進入的。
當有客戶端訪問時,要明確是哪個客戶端,可通過accept()獲取已連接的客戶端對象,並通過該對象與客戶端通過IO流進行數據傳輸。當該客戶端訪問結束,關閉該客戶端。
視頻例程:
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
public class ServerDemo
{
public static void main(String[] args) throws IOException
{
ServerSocket ss = new ServerSocket(10002);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(ip + ":" + text);
//使用客戶端socket對象的輸出流給客戶端返回數據
OutputStream out = s.getOutputStream();
out.write("收到".getBytes());
s.close();
ss.close();
}
}
——4.UDP
將數據及源和目的封裝成數據包中,不需要建立連接。
每個數據報的大小在限制在64k內。
因無連接,是不可靠協議。
不需要建立連接,速度快。
應用案例:QQ、FeiQ聊天、在線視頻用的都是UDP傳輸協議
UDP發送端視頻例程:
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
public class UDPSendDemo
{
public static void main(String[] args) throws Exception
{
System.out.println("發送端啓動......");
/*
* 創建UDP傳輸的發送端。
* 思路:
* 1. 建立udp的socket服務。
* 2. 將要發送的數據封裝到數據包中。
* 3. 通過udp的socket服務將數據包發送出去。
* 4. 關閉socket服務。
*/
//1. udpsocket服務。使用DatagramSocket對象。
//如果發送端端口未指定,就會隨機分配未被使用的端口。
DatagramSocket ds = new DatagramSocket(8888);
//2. 將要發送的數據封裝到數據包中。
String str = "udp傳輸演示,哥們來了!";
//使用DatagramPacket將數據封裝到該對象包中。
byte[] buf = str.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.100"),10000);
//3. 通過udp的socket服務將數據包發送出去,使用send方法。
ds.send(dp);
//4. 關閉資源
ds.close();
}
}
UDP接收端視頻例程:
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
public class UDPReceDemo
{
public static void main(String[] args) throws Exception
{
System.out.println("接收端啓動......");
/*
* 建立UDP接收端的思路。
* 思路:
* 1. 建立udp的socket服務,因爲是要接收數據,必須要明確一個端口號。
* 2. 創建數據包,用於存儲接收到的數據,方便用數據包對象的方法解析這些數
* 3. 使用socket服務的receive方法將接收的數據存儲到數據包中。
* 4. 通過數據包的方法解析數據包中的數據。
* 5. 關閉資源。
*/
//1. 建立udpsocket服務。
DatagramSocket ds = new DatagramSocket(10000);
//2. 創建數據包。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3. 使用接收方法將數據存儲到數據包中。
ds.receive(dp);//阻塞式的。
//4. 通過數據包對象的方法,解析其中的數據,比如:地址,端口,數據內容。
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();
}
}
——5.Socket
1、它被稱之爲插座,相當於港口一樣,是網絡服務提供的一種機制。
2、通信兩端都要有Socket,才能建立服務。
3、網絡通信其實就是Socket間的通信,數據在兩個Socket間通過IO傳輸。
——6.UDP實例
/*
編寫一個聊天程序。
有收數據的部分,和發數據的部分。
這兩部分需要同時執行。
那就需要用到多線程技術。
一個線程控制收,一個線程控制發。
因爲收和發動作是不一致的,所以要定義兩個run方法。
而且這個兩個方法要封裝到不同的類中。
*/
//Udp發送線程
import java.net.*;
import java.io.*;
class UdpSend implements Runnable
{
//定義Socket服務引用
private DatagramSocket ds;
UdpSend(DatagramSocket ds)
{
this.ds=ds;
}
//複寫run方法
public void run()
{
try
{
//2、確定數據,從鍵盤錄入,並把鍵盤錄入的數據封裝成數據包
DatagramPacket dp=null;
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String line=null;
while((line=br.readLine())!=null)
{
byte[] buf=line.getBytes();
dp=new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10000);
//3、通過Socket服務,將已有的數據包發送出去
ds.send(dp);
if ("886".equals(line))
{
break;
}
}
//4、關閉資源
ds.close();
}
catch (Exception e)
{
throw new RuntimeException("發送數據失敗");
}
}
}
//Udp接收線程
class UdpReceive implements Runnable
{
//定義Socket服務引用
private DatagramSocket ds;
UdpReceive(DatagramSocket ds)
{
this.ds=ds;
}
//複寫run方法
public void run()
{
try
{
//一直處於接收狀態
while (true)
{
//2、定義數據包,用於存儲數據
byte[] buf=new byte[1024];
DatagramPacket dp=new DatagramPacket(buf,buf.length);
//3、通過Socket服務,將數據接收並存儲進數據包中
ds.receive(dp);
//4、通過數據包的方法獲取其中的數據
String ip=dp.getAddress().getHostAddress();
String data=new String(dp.getData(),0,dp.getLength());
int port=dp.getPort();
System.out.println("IP:"+ip+"=="+data);
}
//5、關閉資源
//ds.close();
}
catch (Exception e)
{
throw new RuntimeException("接收端接收數據失敗");
}
}
}
class UdpChatDemo
{
public static void main(String[] args)throws Exception
{
new Thread(new UdpSend(new DatagramSocket())).start();
new Thread(new UdpReceive(new DatagramSocket(10000))).start();
}
}
——7、TCP實例
/*
需求:向服務器上傳一個文件,服務返回一條信息
1、客戶端:
源:硬盤上的文件;目的:網絡設備,即網絡輸出流。
若操作的是文本數據,可選字符流,並加入高效緩衝區。若是媒體文件,用字節流。
2、服務端:
源:socket讀取流;目的:socket輸出流。
3、出現的問題:
現象:
a、文件已經上傳成功了,但是沒有得到服務端的反饋信息。
b、即使得到反饋信息,但得到的是null,而不是“上傳成功”的信息
原因:
a、因爲客戶端將數據發送完畢後,服務端仍然在等待這讀取數據,並沒有收到結束標記,就會一直等待讀取。
b、上個問題解決後,收到的不是指定信息而是null,是因爲服務端寫入數據後,需要刷新,才能將信息反饋給客服端。
解決:
方法一:定義結束標記,先將結束標記發送給服務端,讓服務端接收到結束標記,然後再發送上傳的數據。但是這樣定義可能會發生定義的標記和文件中的數據重複,而導致提前結束。
方法二:定義時間戳,由於時間是唯一的,在發送數據前,先獲取時間,發送完後在結尾處寫上相同的時間戳,在服務端,接收數據前先接收一個時間戳,然後在循環中判斷時間戳以結束標記。
方法三:通過socket方法中的shutdownOutput(),關閉輸入流資源,從而結束傳輸流,以給定結束標記。通常用這個方法。
*/
import java.io.*;
import java.net.*;
//客戶端
class TcpClient
{
public static void main(String[] args) throws Exception
{
//創建Socket服務
Socket s=new Socket("127.0.0.1",10000);
//定義讀取流讀取文件數據
BufferedReader br=new BufferedReader(new FileReader("TcpDemo.java"));
//定義目的,將數據寫入到Socket輸出流。發給服務端
//BufferedWriter bwout=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
//定義一個Socket讀取流,讀取服務端返回信息。
BufferedReader brin=new BufferedReader(new InputStreamReader(s.getInputStream()));
String line=null;
while ((line=br.readLine())!=null)
{
pw.println(line);
}
s.shutdownOutput();//關閉客戶端的輸出流。相當於給流中加入一個結束標記-1.
System.out.println(brin.readLine());//接收返回信息
br.close();
s.close();
}
}
//服務端
class TcpServer
{
public static void main(String[] args)throws Exception
{
//創建服務端的ServerSocket服務,並指定監聽端口
ServerSocket ss =new ServerSocket(10000);
//獲取客戶端連接
Socket s=ss.accept();
//獲取客戶端ip
System.out.println(s.getInetAddress().getHostName()+" connected.......");
//讀取Socket讀取流中的數據
BufferedReader brin=new BufferedReader(new InputStreamReader(s.getInputStream()));
//將接收到的數據寫入文件中
PrintWriter out=new PrintWriter(new FileWriter("TcpDemo.txt"),true);
//將返回信息寫入Socket流的寫入流中
BufferedWriter bwout=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line=null;
while ((line=brin.readLine())!=null)
{
out.println(line);
}
//PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
//pw.println("上傳成功");
bwout.write("上傳成功!");
bwout.newLine();//換行
bwout.flush();//刷新
out.close();//關流
s.close();
ss.close();
}
}
——8、URL和URLConnection
1、URL:
URI:範圍更大,條形碼也包含於此範圍
URL:範圍較小,即域名
方法:
1)構造函數:URL(String protocol,String host,int port,String file);//根據指定 protocol、host、port號和 file 創建 URL對象。
2)String getProtocol();//獲取協議名稱
3)String getHost();//獲取主機名
4)int getPort();//獲取端口號
5)String getFile();//獲取URL文件名
6)String getPath();//獲取此URL的路徑部分
7)String getQuery();//獲取此URL的查詢部,客戶端傳輸的特定信息
注:一般輸入網址,是不帶端口號的,此時可進行獲取,通過獲取網址返回的port,若port爲-1,則分配一個默認的80端口,如
int port = getPort();
if(port == -1)
port = 80;
2、URLConnection
方法:
1)URLConnection openConnection();//用URL調用此方法,返回一個 URLConnection 對象,它表示到 URL 所引用的遠程對象的連接。
2)InputStream getInputStream();//獲取輸入流
3)OutputStream getOutputStream();//獲取輸出流