java-網絡通信

網絡通信

  • java最初是作爲一種控制家電的語言被開發出來的。當初設計該語言時,主旨之一是連接機器,而現在仍是如此。
  • java.net包讓java能夠通過網絡進行通信。結合使用輸入輸出流,通過網絡讀寫文件幾乎和讀寫本地文件一樣容易。
  • 要同網絡上的系統進行通信,可以採用三種簡單的方式:
    (1)在小程序中使用URL裝載Web頁和其他資源。
    (2)使用套接字類Socket和ServerSocket,它們建立到主機的標準套接字連接,並通過這種連接執行讀寫。
    (3)調用getInputStream(),該方法建立到URL的連接,並通過該連接來獲取數據。

網絡基本知識

  • 兩臺計算機間進行通訊需要以下三個條件:IP地址、協議、端口號
  • TCP/IP協議是目前世界上應用最爲廣泛的協議,是以TCP和IP爲基礎的不同層次上多個協議的集合,也成TCP/IP協議族、或TCP/IP協議棧
    TCP:Transmission Control Protocol 傳輸控制協議
    IP:Internet Protocol 互聯網協議
  • TCP/IP五層模型
    應用層:HTTP、FTP、SMTP、Telnet等
    傳輸層:TCP ,UDP,UGP
    網絡層: IP,ICMP,IGMP
    數據鏈路層:ARP,RARP
    物理層:網線、雙絞線、網卡等
  • IP地址
        爲實現網絡中不同計算機之間的通信,每臺計算機都必須有一個唯一的標識—IP地址。
  • 端口
        區分一臺主機的多個不同應用程序,端口號範圍爲0-65535,其中0-1023位爲系統保留。
    如:HTTP:80 FTP:21 Telnet:23
    IP地址+端口號組成了所謂的Socket,Socket是網絡上運行的程序之間雙向通信鏈路的終結點,是TCP和UDP的基礎
  • Socket套接字:
        網絡上具有唯一標識的IP地址和端口組合在一起才能構成唯一能識別的標識符套接字。
       Socket原理機制:
         通信的兩端都有Socket
         網絡通信其實就是Socket間的通信
         數據在兩個Socket間通過IO傳輸
  • Java中的網絡支持
         針對網絡通信的不同層次,Java提供了不同的API,其提供的網絡功能有四大類:
         InetAddress:用於標識網絡上的硬件資源,主要是IP地址
         URL:統一資源定位符,通過URL可以直接讀取或寫入網絡上的數據
         Sockets:使用TCP協議實現的網絡通信Socket相關的類
         Datagram:使用UDP協議,將數據保存在用戶數據報中,通過網絡進行通信。

InetAdress

Java提供了InetAddress類來代表IP地址,實現對IP地址的封裝。

獲得本機InetAddress實例
			InetAddress address = InetAddress.getLocalHost();
			System.out.println(address.getHostName());
			System.out.println(address.getHostAddress());
獲得其他主機InetAddress實例
			InetAddress address2 =InetAddress.getByName("其他主機名");
			InetAddress address3 =InetAddress.getByName("IP地址");

URL

url是統一資源定位符,對可以從互聯網上得到的資源的位置和訪問方法的一種簡潔的表示,是互聯網上標準資源的地址。互聯網上的每個文件都有一個唯一的URL,它包含的信息指出文件的位置以及瀏覽器應該怎麼處理它。

構造函數
public URL(String spec)
			spec - 將作爲 URL 解析的 String。 
public URL(String protocol, String host, int port,String file,URLStreamHandler handler)
			protocol - 要使用的協議名稱。
			host - 主機名稱。
			port - 主機端口號。
			file - 主機上的文件
			handler - URL 的流處理程序。 
不一定使用全部參數。

http://www.baidu.com
		http://  :代表超文本傳輸協議,通知microsoft.com服務器顯示Web頁,通常不用輸入;
		www  :代表一個Web(萬維網)服務器;
		baidu :爲域名主體
		.com  :com爲頂級域名,com是company的縮寫,是最常用的頂級域名,表示商業網站。
		常見的還有net,cn等
URL的常用方法
try {
			URL url = new URL("https://mp.csdn.net/mdeditor/84201258");
			System.out.println(url.getProtocol());//獲取協議
			System.out.println(url.getHost());//獲取主機
			System.out.println(url.getPort());//獲取端口,如果未設置端口號,則返回 -1
			System.out.println(url.getFile());//獲取此 URL 的文件名。
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

       使用URL讀取網頁內,通過URL對象的openStream()方法可以得到指定資源的輸入流,通過流能夠讀取或訪問網頁上的資源

  try {
		URL url = new URL("http://www.baidu.com");
		StringBuffer sBuffer =new StringBuffer();
		InputStreamReader  iReader =new InputStreamReader(url.openStream(),"UTF-8");
		BufferedReader bReader =new BufferedReader(iReader);
		String data ;
		while((data=bReader.readLine())!=null){
			sBuffer.append(data);
		};
		System.out.println(sBuffer.toString());
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

Tcp編程

1、TCP協議是面向連接的、可靠的、有序的、以字節流的方式發送數據,通過三次握手方式建立連接,形成傳輸數據的通道,在連接中進行大量數據的傳輸,效率會稍低。
2、Java中基於TCP協議實現網絡通信的類
            客戶端的Socket類
             服務器端的ServerSocket類
通信模型
3、Socket通信的步驟
    ① 創建ServerSocket和Socket
    ② 打開連接到Socket的輸入/輸出流
    ③ 按照協議對Socket進行讀/寫操作
    ④ 關閉輸入輸出流、關閉Socket
4、服務器端:
     ① 創建ServerSocket對象,綁定監聽端口
     ② 通過accept()方法監聽客戶端請求
     ③ 連接建立後,通過輸入流讀取客戶端發送的請求信息
     ④ 通過輸出流向客戶端發送鄉音信息
     ⑤ 關閉相關資源

客戶端方法介紹

構造方法概述
Socket() 
          通過系統默認類型的 SocketImpl 創建未連接套接字
Socket(InetAddress address, int port) 
          創建一個流套接字並將其連接到指定 IP 地址的指定端口號。
			address - IP 地址。
			port - 端口號。
普通方法概述
void connect(SocketAddress endpoint) 
          將此套接字連接到服務器。 
 void setSoTimeout(int timeout) 
         啓用/禁用帶有指定超時值的 SO_TIMEOUT,以毫秒爲單位。 當時間超過timeout毫秒
         時,將觸發InterruptedIOException異常,因此可以在try-catch塊關閉套接字或再次
         讀寫數據。
 boolean isConnected() 
          返回套接字的連接狀態。 

注意:
(1)創建套接字時,應設置其超時值, 當時間超過超時值時,將觸發InterruptedIOException異常,因此可以在try-catch塊關閉套接字或再次讀寫數據。如果沒有設置,它可能一直等待下去。
(2)通常將網絡操作放在單獨的線程中,並讓它和程序的其他部分分開運行。

服務器端方法概述

構造函數
ServerSocket() 
          創建非綁定服務器套接字。 
ServerSocket(int port) 
          創建綁定到特定端口的服務器套接字。 
普通方法
 Socket accept() 
          偵聽並接受到此套接字的連接。 
 void bind(SocketAddress endpoint) 
          將 ServerSocket 綁定到特定地址(IP 地址和端口號)。 
 InetAddress getInetAddress() 
          返回此服務器套接字的本地地址。 
void setSoTimeout(int timeout) 
          通過指定超時值啓用/禁用 SO_TIMEOUT,以毫秒爲單位。
boolean isBound() 
          返回 ServerSocket 的綁定狀態。  

(1)創建服務器端時,要綁定端口號,可以在構造函數或者利用bind()函數綁定,服務器端監聽這個端口,客戶端通過IP地址和這個端口連接到服務器上。
(2)端口0-1023的用途由Internet地址分配機構使用。
(3)要先運行服務器端,在運行客戶端。
實例:
   創建客戶端和服務端,客戶端連接到服務器時,服務端發送當前時間,並斷開連接,客戶端顯示出來時間。

客戶端結果
服務器端代碼

package CommDemo;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import javax.xml.crypto.Data;

public class ServerDemo  extends Thread{
	private ServerSocket myServer =null;
	 public ServerDemo(int port){
		  try {
					myServer =new ServerSocket(port);
					System.out.println("服務器開始運行");
				} catch (IOException e) {
					e.printStackTrace();
					System.err.println("could not ....");
				}
	 }
	 
	 public void run(){
	     //對客戶端的連接要放在多線程裏,否則一直等待連接。
		 Socket  mySocket =null;
		  while(true){
		   //死循環,不斷檢測有無連接的客戶端
			   try {
				mySocket =myServer.accept();
				System.out.println("客戶端連接成功");
				BufferedOutputStream bOutputStream =new    	
				BufferedOutputStream(mySocket.getOutputStream());
				Date date = new Date();
				PrintWriter pWriter =new PrintWriter(bOutputStream,false);
				pWriter.println(date);
				pWriter.flush();
				pWriter.close();
				bOutputStream.close();
				mySocket.close();
			
			} catch (IOException e) {
				e.printStackTrace();
				System.err.println("無法連接到客戶端");
				
			}
		  }
	 }
	public static void main(String[] args) {
		ServerDemo  myServerDemo =new ServerDemo(6666);
		myServerDemo.start();
	}
}

客戶端程序

package CommDemo;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;

public class ClientDemo extends Thread implements ActionListener {
  
	 private Socket mySocket = null;
	 private   JButton connect =new JButton("連接服務器");
      private  JButton disconnect =new JButton("斷開服務器");
      BufferedInputStream bInputStream =null;
      JTextArea myArea =null;
	 public ClientDemo(){
			 //初始化界面
			   JFrame frame = new JFrame("客戶端");
			   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		      myArea=new JTextArea("",5,10);
		       frame.getContentPane().add(myArea, "Center");
		       JPanel panel =new JPanel();
		       panel.setLayout(new FlowLayout());
		       connect.addActionListener(this);
		       disconnect.addActionListener(this);
		       panel.add(connect);
		       panel.add(disconnect);
		       frame.getContentPane().add(panel, "South");
		       frame.pack();
		       frame.setVisible(true); 
	 }
	public static void main(String[] args) {
		ClientDemo myClientDemo =new ClientDemo();  
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		  if(e.getSource()==connect){
				try {
					StringBuffer sBuffer = new StringBuffer();
					mySocket = new Socket("127.0.0.1", 6666);//連接本地IP的6666端口
					mySocket.setSoTimeout(10000);//設置超時值
					bInputStream=new BufferedInputStream(mySocket.getInputStream());
					   int data =-1;
						while((data=bInputStream.read())!=-1){
						  sBuffer.append((char)data);
					   }
					myArea.setText(sBuffer.toString());
				} catch (IOException e1) {
					e1.printStackTrace();
				}	
		  }else if(e.getSource()==disconnect){
				try {
					myArea.setText("已斷開連接");
					bInputStream.close();
					mySocket.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}		
		  }
	}
}

UDP編程

  • UDP協議(用戶數據報協議)是無連接的、不可靠的、無序的,速度快,進行數據傳輸時,首先將要傳輸的數據定義成數據報(Datagram),大小限制在64k,在數據報中指明數據索要達到的Socket(主機地址和端口號),然後再將數據報發送出去
  • UDP通信中使用的兩個類:
    DatagramPacket類:表示數據報包
    DatagramSocket類:進行端到端通信的類
  • 使用UDP進行編程的步驟
    客戶端:
    (1)使用DatagramPacket包裝要發送的數據。
    DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)構造數據報包,用來將長度爲 length 偏移量爲 offset 的包發送到指定主機上的指定端口號。address是要發送的服務器的ip地址,port是服務器的端口號。
    (2)建立DatagramSocket對象,通過方法DatagramSocket(int port)
    創建數據報套接字並將其綁定到本地主機上的指定端口。這裏的port是客戶端的端口號,服務器可以根據客戶端的IP和端口,向客戶端反饋信息。
    (3) void send(DatagramPacket p)
    從此套接字發送數據報包 。

服務器端
(1)建立DatagramSocket對象,通過方法DatagramSocket(int port)
創建數據報套接字並將其綁定到本地主機上的指定端口。
(2)DatagramPacket(byte[] buf, int length) 構造 DatagramPacket,用來接收長度爲 length 的數據包。
(3)使用DatagramSocket對象的 void receive(DatagramPacket p)方法從此套接字接收數據報包。
(4)將此數據包轉換成適當的數據形式
(5)通過int getPort()返回客戶端的端口。InetAddress getInetAddress()返回客戶端的IP地址。 從而服務端也可向客戶端發送數據包,發送步驟等同於客戶端發送數據包。

示例
客戶端向服務器端發送當前時間,服務器端顯示收到的時間。
運行結果
運行結果
上面是兩次客戶端發來結果時,服務器端的顯示

服務器端

package CommDemo;

import java.awt.BorderLayout;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

import javax.swing.JFrame;
import javax.swing.JTextArea;

public class UdpServerDemo extends JFrame implements Runnable{
	 private DatagramSocket serverSocket = null;
	 private DatagramPacket receivePacket =null;
	 private byte bytes[];
	 JTextArea textArea =null;
	public UdpServerDemo(int port){
		//建立界面
		super("服務器端");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(new BorderLayout());
		textArea=new JTextArea("",10,50);
		getContentPane().add(textArea, "Center");
       pack();
		setVisible(true);
		
		try {
			System.out.println("服務器正在運行");
			serverSocket =new DatagramSocket(port);
			bytes =new byte[1024];  
			receivePacket =new DatagramPacket(bytes, bytes.length);	
			
		} catch (SocketException e) {
		   System.out.println("couldnot");
			e.printStackTrace();
		}
	}
	public  void run(){
		while(true){
			 try {
					serverSocket.receive(receivePacket);
					System.out.println("客戶端已連接");
					Object object  =bytesToObject(receivePacket.getData());
					textArea.append("客戶端發來:"+object .toString()+"\n");
				} catch (IOException e) {
					System.out.println("連接失敗");
					e.printStackTrace();
				}
		}	 
	}
	//將字節數組轉換成對象
	public Object bytesToObject(byte bytes[]){ 
	      Object obj =null;
		  try {
		   ByteArrayInputStream bArrayInputStream =new ByteArrayInputStream(bytes);
			  ObjectInputStream objectInputStream =new 
			  ObjectInputStream(bArrayInputStream);
	          obj =objectInputStream.readObject();
		  } catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return obj;  
	}
	public static void main(String[] args) {
		
		
		UdpServerDemo myServerDemo =new UdpServerDemo(6667);
		Thread serverThread = new Thread(myServerDemo);
		serverThread.start();
		
	}
}

客戶端

package CommDemo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Date;

import javax.swing.JFrame;

public class UdpClientDemo {

	DatagramSocket  clientSocket =null;
	DatagramPacket  clientPacket =null;
	byte bytes [];
	InetAddress sendAddress =null;
	public UdpClientDemo(int port){
		try {
			clientSocket =new DatagramSocket(port);
			sendAddress = InetAddress.getByName("127.0.0.1");
			bytes=new byte[1024];
			bytes = ObjectToBytes(new Date());
			clientPacket =new DatagramPacket(bytes,bytes.length,sendAddress,6667);
			clientSocket.send(clientPacket);
			clientSocket.close();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
		
			e.printStackTrace();
		}
	}
	public byte[] ObjectToBytes(Object obj){
		   ByteArrayOutputStream bArrayOutputStream =new ByteArrayOutputStream();
		   try {
			ObjectOutputStream objectOutputStream =new ObjectOutputStream(bArrayOutputStream);
		   objectOutputStream.writeObject(obj);
		   bytes =bArrayOutputStream.toByteArray();
		   } catch (IOException e) {
			e.printStackTrace();
		}
		  return  bArrayOutputStream.toByteArray();
       
	}
	
	public static void main(String[] args) {
	UdpClientDemo myClientDemo = new UdpClientDemo(6668);
	}
}

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