---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS開發</a>、<a href="http://edu.csdn.net"target="blank">.Net培訓</a>、期待與您交流! ----------------------
1、網絡模型:OSI參考模型和TCP/IP參考模型
圖示:
一般來說開發處於傳輸層和網際層,應用層爲:FTP和HTTP協議等,傳輸層爲:UDP和TCP等,網際層爲:IP。
通常用戶操作的是應用層,而編程人員需要做的是傳輸層和網際層,用戶在應用層操作的數據,經過逐層封包,最後到物理層發送到另一個模型中,再進行逐層解包,圖示爲:
2、網絡通信三要素:IP地址,端口號,傳輸協議
A、IP地址
a、它是網絡中的設備標識
b、不易記憶,可用主機名錶示,兩者存在映射關係
c、本機迴環地址:127.0.0.1,主機名爲:localhost。
IP地址:java中對應的是InetAddress類,存在於java.net包中。
InetAddress類:
(一)無構造函數,可通過getLocalHost()方法獲取InetAddress對象,此方法是靜態的,返回本類對象。
InetAddress i = InetAddress.getLocalHost();
(二)方法:
1)static InetAddress getByName(String host):獲取指定主機的IP和主機名。(最好用ip地址去獲取,主機名需要解析)
2)static InetAddress[] getAllByName(String host):在給定主機名的情況下,根據系統上配置的名稱服務返回IP地址所組成的數組。返回對象不唯一時,用此方法。
3)String getHostAddress():返回IP地址字符串文本形式,以IP地址爲主。
4)String getHostName():返回IP地址主機名。
(三)如何獲取任意一臺主機的IP地址對象:
1)功能:返回InetAddress對象
2)對於任意主機,需要指定傳入主機名的參數
注意:如果IP地址和對應的主機名,這種映射關係沒有在網絡上,就不會解析成功,返回的還是指定的IP。
示例:
B、端口號:
a、用於標識進程的邏輯地址,不用進程的標識。
b、有效端口:0 ~65535,系統使用或保留的端口是:0~ 1024。
C、傳輸協議:
即通信規則,包含TCP和UDP協議
UDP
是面向無連接,明確了對方的端口,無論在不在網上,只管傳輸,不在就會丟失數據。只求速度,應用於網絡視頻會議和聊天等應用程序中。
協議特點:
a、面向無連接,即將數據及源和目的封裝成數據包中,不建立鏈接的發送
b、每個數據包的大小限制在64K之內
c、因無連接,是不可靠的協議
d、不建立連接,速度快。
TCP
是面向連接的,必須連接成功才能傳輸數據,應用於下載等程序上
協議特點:
a、面向連接,在建立連接後,形成傳輸數據的通道
b、在連接中進行大數據量的傳輸
c、通過三次握手完成連接,是可靠的協議
d、必須建立連接,效率稍慢
三次握手:第一次本方發送請求,第二次對方確認連接,第三次本方再次確認連接成功。
3、通信的步驟:
1)找到IP地址
2)數據要發送到對象指定應用程序,爲標識這些應用程序,所以給這些網絡應用程序都用數字標識,爲方便稱呼這個數字,叫做端口,即邏輯端口。
3)定義通信規則,稱之爲協議。國際組織定義了通用協議,即TCP/IP。
注意:必須要有數字標識才能將數據發送到應用程序上。
第二講 傳輸協議
一、Socket
1、它被稱之爲插座,相當於港口一樣,是網絡服務提供的一種機制。
2、通信兩端都要有Socket,才能建立服務。
3、網絡通信其實就是Socket間的通信,數據在兩個Socket間通過IO傳輸。
二、UDP傳輸
1、通過類DatagramSocket,此類表示用發送和接收數據包的套接字,即Socket。
2、方法:
1)創建 UDPSocket發送服務對象:
DatagramSocket(),不指定端口。DatagramSocket(int port),指定端口。
2)發送:void send(DatagramPacket p)
3)接收:void receive(DatagramPacket p)
其中DatagramPacket:數據報包用來實現無連接包投遞服務的,每條報文僅根據該包中包含的信息從一臺機器路由到另一臺機器中。凡是帶地址(InetAddress)的都是用於發送包的。
3、步驟
1)發送數據:
a、建立UDPSocket服務,在此無需指定端口,也可以將端口加入。如果不指定的話,系統會隨機分配一個端口,如第一次運行時端口爲1093,那麼第二次就會順延爲1094,再運行會一直順延,因爲之前的端口還沒有得到釋放,所以會順延端口號值。
b、提供數據,並將數據封裝到數據包中
c、通過socket服務的發送功能,將數據包發送出去
d、關閉資源
2)接收數據:
a、定義UDPSocket服務。通常會監聽一個端口,其實就是給這個接收網路應用程序定義數字標識,方便於明確哪些數據過來該應用程序可以處理。
b、定義一個數據包,用來存儲接收到的字節數據,因爲數據包對象中有更多功能可以提取字節數據中的不同數據信息。
c、通過socket服務的receive方法接收到的數據存入已定義好的數據包中
d、通過數據包對象的特有功能,將這些不同的數據取出,打印在控制檯上
e、關閉資源
在定義接收數據的方法中,仍會在DatagramSocket構造函數中傳入DatagramPacket的參數,這是因爲收到的數據太多,需要解析,通過將數據封裝成對象,易於解析,所以需要傳入參數。
注意:
1、發送端與接收端是兩個獨立的運行程序。
2、在發送端,要在數據包對象中明確目的地IP及端口。
3、在接收端,要指定監聽的端口。
示例:
練習一
練習二
三、TCP傳輸
1、TCP分客戶端和服務端。客戶端對應的對象是Socket,服務端對應的對象是ServerSocket。
2、方法:
1)創建客戶端對象:
Socket():創建空參數的客戶端對象,一般用於服務端接收數據
Socket(String host,int port),指定要接收的IP地址和端口號
2)創建服務端對象:ServerSocket(int port):指定接收的客戶端的端口
3)Socket accept():監聽並接受到此套接字的連接
4)void shutdownInput():此套接字的輸入流至於“流的末尾”
5)void shutdownOutput():禁用此套接字的輸出流
6)InputStream getInputStream():返回此套接字的輸入流,Socket對象調用
7)OutputStream getOutputStream():返回套接字的輸出流,Socket對象調用
3、基本思路
客戶端:
1)客戶端需要明確服務器的ip地址以及端口,這樣纔可以去試着建立連接,如果連接失敗,會出現異常。
2)連接成功,說明客戶端與服務端建立了通道,那麼通過IO流就可以進行數據的傳輸,而Socket對象已經提供了輸入流和輸出流對象,通過getInputStream(),getOutputStream()獲取即可。
3)與服務端通訊結束後,關閉Socket。
服務端:
1)服務端需要明確它要處理的數據是從哪個端口進入的。
2)當有客戶端訪問時,要明確是哪個客戶端,可通過accept()獲取已連接的客戶端對象,並通過該對象與客戶端通過IO流進行數據傳輸。
3)當該客戶端訪問結束,關閉該客戶端。
4、步驟
客戶端:
通過查閱Socket對象的API文檔,發現在該對象在建立時,就可去連接指定主機,因爲TCP是面向連接的,所以在建立Socket服務時,就要有服務端存在,並連接成功,形成通路後,再通過該通道進行數據的傳輸。
1)創建Socket服務,並指定要連接的主機端口。通路一建立,就會產生Socket流(包括輸入流和輸出流),通過方法獲取
2)爲了發送數據,應獲取Socket中的輸出流,如果要接收服務端的反饋信息,還需要獲取Socket的輸入流
3)通過輸出流的write()方法將要發送的數據寫入到流中
4)關閉Socket流資源
服務端:
服務器套接字等待請求通過網絡傳入。它基於該請求執行某些操作,然後可能向請求者返回結果。需監聽一個端口。
1)建立服務端的Socket服務,並監聽一個端口。通過ServerSocet帶端口參數的構造函數
2)獲取連接過來的客戶對象,通過ServerSocket的accept()方法,此方法是阻塞式的,如果服務端沒有連接到就會一直等待
3)客戶端如果發過來數據,則服務端要使用對應的客戶端對象,並獲取到該客戶端對象的讀取流讀取發過來的數據,並輸出到指定目的地。
4)關閉服務端(可選)。一般服務端是常開的,因爲在實際應用中,隨時有客戶端在請求連接和服務。但這裏需要定時關閉客戶端對象流,避免某一個客戶端長時間佔用服務器端。
示例:
練習一
練習二
/*
需求:向服務器上傳一個文件,服務返回一條信息
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();
}
}
第三講 應用
一、用TCP客戶端併發上傳圖片
1、一對一(單線程)上傳的思路:
客戶端
a、服務端點。
b、讀取客戶端已有的圖片數據
c、通過Socket輸出流將數據發給服務端
d、讀取服務端反饋信息。
e、關閉
服務端
a、服務端服務,並監聽窗口
b、獲取客戶端對象,並獲取客戶ip
c、讀取客戶端輸入流數據
d、寫入文件
e、用客戶端輸出流反饋信息
f、關流
2、單線程的服務端有個侷限性。當A客戶端連接上以後,被服務端獲取到。服務端執行具體流程。這時B客戶端連接,只能等待。因爲服務端還沒有處理完A客戶端的請求。還沒有循環回來執行下一次accept方法。所以,暫時獲取不到B客戶端對象。
那麼爲了可以讓多個客戶端同時併發訪問服務端。服務端最好就是將每個客戶端封裝到一個單獨的線程中,這樣,就可以同時處理多個客戶端請求。
如何定義線程呢?
只要明確了每一個客戶端要在服務端執行的代碼,將該代碼存入run方法即可。
代碼:
二、客戶端併發登錄
客戶端通過鍵盤錄入用戶名,服務端對這個用戶名進行校驗。
如果該用戶存在,在服務端顯示xxx,已登陸;並在客戶端顯示xxx,歡迎光臨。
如果用戶不存在,在服務端顯示xxx,嘗試登陸;並在客戶端顯示xxx,該用戶不存在。
最多就登錄三次。
代碼:
import java.io.*;
import java.net.*;
//客戶端
class LoginClient
{
public static void main(String[] args) throws Exception
{
//創建服務
Socket s=new Socket("localhost",10000);
//鍵盤錄入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//用Socket服務輸出流寫入數據
PrintWriter out =new PrintWriter(s.getOutputStream(),true );
//接收服務器返回的信息
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));
String line=null;
for(int x=0;x<3;x++)
{
line=br.readLine();//讀取鍵盤錄入
if (line==null)
{
break;//如果鍵盤沒有輸入,則直接結束
}
out.println(line);//將數據寫入流中
String info=in.readLine();//讀取返回信息
System.out.println(info);
if (info.contains("歡迎"))//---------------
{
break;//如果登錄成功,就跳出循環
}
}
br.close();//關流
s.close();
}
}
//服務端
class LoginServer
{
public static void main(String [] args)throws Exception
{
//創建服務,監聽端口
ServerSocket ss=new ServerSocket(10000);
while (true)
{
//獲取客戶端對象
Socket s=ss.accept();
//客戶端執行線程
new Thread(new LoginThread(s)).start();
}
//ss.close();
}
}
//利用多線程實現併發登錄
class LoginThread implements Runnable
{
private Socket s;
LoginThread(Socket s)
{
this.s=s;
}
public void run()
{
//獲取客戶端ip
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+" connected.....");
try
{
for (int x=0;x<3 ;x++ )
{
//通過客戶端的讀取流讀取數據
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));
//讀取數據庫中的數據,這裏用文件來表示數據庫
BufferedReader br=new BufferedReader(new FileReader("users.txt"));
String line=in.readLine();//讀取客戶端數據
if (line==null)//--------------
{
break;//如果客戶端沒有發送數據,則跳出循環
}
String data=null;
boolean flag=false;//設置標記
//讀取數據庫中的用戶數據
while ((data=br.readLine())!=null)
{
if (line.equals(data))
{
flag=true;//如果用戶存在,則將標記設爲true
break;
}
}
//將數據寫入到指定文件中
PrintWriter out=new PrintWriter(s.getOutputStream(),true);
if (flag)
{
System.out.println(line+",已登陸!");
out.println(line+",歡迎光臨!");
break;//-----------
}
else
{
System.out.println(line+",嘗試登陸!");
out.println(line+",用戶名不存在!");
}
}
s.close();//關流
}
catch (Exception e)
{
throw new RuntimeException("用戶登陸失敗");
}
}
}
三、客戶端和服務的瀏覽器演示
瀏覽器是一個標準的客戶端,它可以對服務端傳送過來的數據消息進行解析,把符合應用層協議的消息部分解析後,將頭信息拆包掉,傳送到應用層,只保留了正確的正文主題部分顯示在主體部分上。
而由於使用java編譯是在傳輸層和網際層處理的,所以,會接受到全部的消息,包含了頭消息。而瀏覽器處於應用層,已將發送來的頭消息去除,只留下了主體信息。
示例:
自定義服務器,用瀏覽器訪問:
四、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();//獲取輸出流
示例:
小知識點
1、Socket類的構造函數中,有一個空參數的構造函數:
Socket()//通過系統默認類型的 SocketImpl創建未連接套接字對象
可以通過connect(SocketAddress endpoint)方法來連接服務器。而SocketAddress是一個抽象類,它的子類InetSocketAddress實現了IP套接字地址(IP地址+端口號)。所以就可以連接到服務器了。
2、ServerSocket對象中的構造函數:
ServerSocket(int port,int backlog),其中的backlog表示隊列的最大長度,即最多連入客戶端的個數,即最大連接數。
3、在瀏覽器輸入網址訪問一臺主機所做的操作:
如輸入http://61.135.169.125,可以直接連接此ip的主機,而我們一般是輸入主機名:http:/www.baidu.ocm(百度主機對應的ip地址就是:61.135.169.125),那麼此時瀏覽器做了神馬操作呢?
也就是說如何通過主機名獲取IP地址,從而連接到這臺主機呢?這就需要將主機名翻譯成IP地址,即域名解析:DNS
在進行訪問的時候,會先在本地的hosts文件(c:\windows\system32\drivers\ext\host)中找對應的映射。若有,則直接返回請求;若無,則到公網的映射列表即DNS中找對應的映射,找到後,將主機名對應的IP地址返回給本機,本機通過這個IP地址找到對應的服務器。
示意圖:
host應用:可屏蔽一些惡意網址,即將對應的映射關係寫入hosts中,將IP地址改爲本機的迴環地址,那麼會直接找到hosts,就不會將請求發送出去了。
---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS開發</a>、<a href="http://edu.csdn.net"target="blank">.Net培訓</a>、期待與您交流! ----------------------