---------------------- ASP.Net+Android+IOS開發、.Net教育訓練、期待與您交流! ----------------------
網絡通信的三要素:
(1):IP位址
(2):通信端口
(3):通信協定
Ip位址:ip位址實際上是有4個位元組共32位的二進制來表示的,但是這樣不便于記憶,那麼把這32位分割成4分,每一位元組8位,再轉成十進制表示。2^8=256-1,範圍在0~255,255是廣播位址
分A,B,C類IP位址
子網路遮罩的作用:為了辨別網絡号
示例:255.255.255.0
A類IP位址:1個網絡号+3個主機号
B類IP位址:2個網絡号+2個主機号
C類IP位址:3個網絡号+1個主機号
類 InetAddress
此類表示網際網路協定 (IP) 位址
static InetAddress getLocalHost():擷取本機的IP位址對象,傳回一個InetAddress對象
String getHostAddress():擷取 IP 位址字元串,傳回String類型
String getHostName():擷取此 IP 位址的主機名,傳回String類型
static InetAddress getByName(String host):在給定主機名的情況下确定主機的 IP 位址。主機名可以是機器名(如 "
java.sun.com
"),也可以是其 IP 位址的文本表示形式(如"192.168.10.10")
注意:主機名有可能重複,但IP位址是唯一的(在同一網内),是以此方法推薦使用IP位址
boolean isReachable(int timeout): 測試是否可以達到該位址(能否在指定時間内連接配接上該位址),timeout 為調用中止前的時間(以毫秒為機關)
static InetAddress[] getAllByName(String host):在給定主機名的情況下,根據系統上配置的名稱服務傳回其 IP 位址所組成的數組。
用法:getAllByName(“www.baidu.com”),一個域名可以對應多個主機,百度域名可能有多個(主機)伺服器,是以這個方法是得到百度的所有伺服器的IP位址對象
網絡通訊也叫Socket通訊
不同的協定用不同的socket
UDP協定: DatagramSocket(發送與接收), DatagramPacket(資料包)
UDP沒有用戶端與服務端之分
發送端與接收端都用DatagramSocket
TCP協定:Socket(用戶端), ServerSocket(服務端)
TCP分用戶端,服務端,兩者有各自的Socket
UDP協定與TCP協定的差別
UDP
•
将資料及源和目的封裝成資料包中,不需要建立連接配接
•
每個資料報的大小在限制在64k内
•
因無連接配接,是不可靠協定,會出現丢包(帶寬或CPU能力不足)
•
不需要建立連接配接,速度快(效率比tcp高)
TCP
•
建立連接配接,形成傳輸資料的通道。
•
在連接配接中進行大資料量傳輸,資料沒有大小限制
•
通過三次握手完成連接配接,是可靠協定
•
必須先建立連接配接,效率會稍低
建立UDP發送端
思路與步驟:
1:定義Socket服務(DatagramSocket)
DatagramSocket(int port) //如果沒有指定端口,那麼由CPU指定
建立資料報套接字并将其綁定到本地主機上的指定端口。
2:定義一個資料包,封裝需要發送的資料
DatagramPacket(byte[] buf, intlength, InetAddress address, int port)
構造資料報包,用來将長度為length 的包發送到指定主機上的指定端口号。
3:通過Socket服務發送資訊
send(DatagramPacket p)
從此套接字發送資料報包。
4:關閉資源
close()
核心代碼:
DatagramSocket ds = new DatagramSocket(8888);
byte[] buf = "我的資訊".getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.10.10"),10000);
ds.send(dp);
ds.close();
建立一個UDP接收端
思路與步驟
1:建立一個Socket服務
DatagramSocket(int port)
建立資料報套接字并将其綁定到要監聽(要接收資料的)的指定端口。
2:定義一個資料包,用于存儲到接收到的資料
DatagramPacket(byte[] buf, intlength)
構造DatagramPacket,用來接收長度為 length 的資料包。
3:通過Socket服務的receive()将接收到的資料存放到資料包中
receive(DatagramPacket p)
從此套接字接收資料報包。
4:通過資料包特有的方法擷取接收到的資料
5:關閉資源
核心代碼:
DatagramSocket ds = new DatagramSocket(10000);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(ip+"::"+data+"::"+port);
ds.close();
建立一個TCP的用戶端
思路與步驟:
1:建立用戶端Socket服務,指定要連接配接的主機位址和服務端口
2:為了發送資料,應該擷取Socket流中的輸出流
3:往輸出流寫資料(與UDP這一步驟有差別,不需要寫發送資料的語句,隻需把資料寫入流即可)
4:關閉Socket流對象
核心代碼
Socket s = new Socket("192.168.1.254",10003);
//建立用戶端Socket服務,指定要連接配接的主機位址和服務端口
OutputStream out = s.getOutputStream();
//為了發送資料,應該擷取Socket流中的輸出流
out.write("我是服務端".getBytes());
//往輸出流寫資料
s.close();
//關閉流
建立一個TCP服務端
思路與步驟:
1:建立一個服務端socket的服務,并監聽一個端口
2:通過accept方法擷取連接配接過來的用戶端的對象
3:擷取用戶端發過來的資料,要使用用戶端對象的讀取流來讀取
4:斷開連着的用戶端,免得占用資源
核心代碼
ServerSocket ss = new ServerSocket(10003);
//通過accept方法擷取連接配接過來的用戶端的對象
Socket s = ss.accept();//accept()傳回的是一個Socket類型的對象
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip);
//擷取用戶端發過來的資料,要使用用戶端對象的讀取流來讀取
InputStream in = s.getInputStream();//得到用戶端對象後,擷取讀取流
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
s.close();//斷開連着的用戶端,免得占用資源
建立TCP用戶端與服務端互訪:
用戶端發送資料到服務端,服務端接收到資料後,傳回自定義資料
用戶端代碼:
class TransClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("192.168.1.254",10005);
//定義讀取鍵盤資料的流對象。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//定義目的,将資料寫入到socket輸出流。發給服務端。
BufferedWriter bufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//定義一個socket讀取流,讀取服務端傳回的大寫資訊。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufOut.write(line);
bufOut.newLine();
bufOut.flush();
String str =bufIn.readLine();
System.out.println("server:"+str);
}
bufr.close();
s.close();
}
}
服務端代碼:
class TransServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10005);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
//讀取socket流中的資料。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//目的。socket輸出流。将大寫資料寫入到socket輸出流,并發送給用戶端。
BufferedWriter bufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line = null;
while((line=bufIn.readLine())!=null)
{
System.out.println(line);
bufOut.write(line.toUpperCase());
bufOut.newLine();
bufOut.flush();
}
s.close();
ss.close();
}
}
在建立TCP用戶端與服務端互訪的時候,有如下細節需要注意
(1) 用戶端在寫入資料之後,需要重新整理,才能被發送
(2) 服務端在讀資料的時候,使用的是緩沖讀取流,readLine()判斷資料結束的标記是回車符\r\n,是以要在用戶端發送資料後再添加一個換行:newLine();
(3) 同理,服務端發送傳回資料的時候,也要重新整理和換行:newLine();
介紹一個新的帶自動重新整理的輸出流對象:PrintWrite
并且PrintWrite的特有輸出方法println()能夠自動加上換行符
那麼上述代碼核心部分
BufferedWriterbufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
修改為:PrintWriter out = newPrintWriter(s.getOutputStream(),true);
bufOut.write(line.toUpperCase());
bufOut.newLine();
bufOut.flush();
修改為:out.println(line.toUpperCase());
用戶端從服務端下載下傳檔案
服務端支援多台主機同時連接配接下載下傳,并統計被下載下傳多少次,同一IP位址下載下傳多次算一次
服務端代碼示例:
public class MyServer {
public static void main(String[] args) throws Exception {
@SuppressWarnings("resource")
ServerSocket server = new ServerSocket(10000);
ClientThread ct = null;
while (true) {
Socket client = server.accept();
//accept()是阻塞式方法,如果有連接配接,才會往下執行
ct = new ClientThread(client);//注意多線程的寫法
Thread t = new Thread(ct);
t.start();
}
}
}
class ClientThread extends Thread {
private Socket client;
public static int num = 0;
public ClientThread(Socket client) {
this.client = client;
}
HashSet<InetAddress> hashset = new HashSet<InetAddress>();
@Override
public void run() {
FileInputStream in = null;
try {
OutputStream out = client.getOutputStream();
in = new FileInputStream(new File("d:\\test\\IMG_0309.JPG"));
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
out.flush();
}
if (!(hashset.contains(client.getInetAddress()))) {
num++;
}
System.out.println("下載下傳了" + num + "次");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
用戶端代碼:
public class DownloadPic {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Socket socket = new Socket("192.168.10.10", 10000);
InputStream in = socket.getInputStream();
FileOutputStream fos = new FileOutputStream(new File(
"d:\\test\\我下載下傳的pic.jpg"));
byte[] buf = new byte[1024];
int length = 0;
while ((length = in.read(buf)) != -1) {
fos.write(buf, 0, length);
fos.flush();
}
fos.close();
socket.close();
}
}
用戶端往服務端上傳檔案
要注意的地方:要注意結束标記,服務端讀取的結束标記,否則會一直阻塞
可以用Socket的方法,shutDownOutput()關閉用戶端的輸出流,執行此方法是通知服務端,檔案讀完了,可以結束服務端讀取的循環
問題:為什麼用戶端從服務端下載下傳檔案的時候,不需要shutDownOutput()也可以正常執行?
答:
下載下傳:服務端使用自定義輸入流讀取需要被下載下傳的檔案,然後交給socket輸出流,當輸入流讀到流末尾的時候會自動結束讀取循環,socket輸出流沒有寫入結束符号。但是服務端會繼續往下執行,會關閉socket對象,如果socket被關閉,用戶端會收到通知,會自動結束用戶端的socket流
上傳:因為用戶端用自定義的輸入流中讀取需要被上傳的檔案,交給socket的輸出流,自定義的輸入流讀完資料會結束讀取,但是不會寫入結束符号,會結束while循環,繼續往下執行,會關閉socket,服務端從socket讀取資料的時候,也不會讀到結束符号,是以服務端會一直等待,這時候必須要用戶端發送一個結束的标記,提示服務端結束讀取。
URLConnection(重點)屬于應用層
URL url = new URL(www.baidu.com:8080/web/demo.html);
URLConnection conn= url.openConnection();
//此時openConnection()方法會傳回一個URLConnection類型的對象
該對象的作用是把Socket封裝在了内部,起的是Socket的連接配接作用
---------------------- ASP.Net+Android+IOS開發、.Net教育訓練、期待與您交流! ----------------------
詳細請檢視:http://edu.csdn.net