網絡通信
- 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);
}
}