16、網絡編程


十六、網絡編程

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();//獲取輸出流






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章