天天看點

JAVA網絡程式設計(TCP/UDP) 基礎

基本目錄

    |-網絡程式設計概述

    |-InetAddress類

    |-UDP程式設計

    |-TCP程式設計

   └-URL(URLConnection)

執行個體01 擷取指定主機資訊

執行個體02 UDP發送端

執行個體03 UDP接收端

執行個體04 UDP通信(用鍵盤錄入方式傳輸資料)

執行個體05 UDP聊天

執行個體06 TCP伺服器-轉換大寫字母

執行個體07 TCP上傳檔案

執行個體08 TCP上傳圖檔到伺服器

執行個體09 優化TCP上傳圖檔

執行個體10 TCP用戶端并發登入

執行個體11 一個簡單的TCP伺服器

執行個體12 擷取URL的各個參數

一、網絡程式設計概述

1、伺服器和用戶端

為了使兩台計算機之間能夠進行通信,必須為這兩台計算機建立一個網絡,

将這兩台計算機進行連接配接,把一台作為用戶端,一台作為伺服器。

用戶端:是指發送請求資訊的計算機或程式。

伺服器:是指能提供資訊的計算機或程式。

但有時候兩台計算機都是互相發送資訊,互相接收資訊的,是以很難區分。

為了保證能将兩台或更多的計算機之間能進行通信,必須有某種互相遵守的條約,這就是協定。

例如網際網路使用IP協定,這種協定使用4個位元組來辨別網絡中的一台計算機。

在公司内部區域網路中,每台機器都有一個IP位址如192.168.0.1,這就是IP協定位址,在一個網

段中它必須是唯一的。

2、TCP與UDP

TCP (Transmission Control Protocol)

是指傳輸控制協定,它和IP協定一起組成TCP/IP協定,TCP協定負責資料或檔案的拆包與封

包,而IP協定負責發送和接收資料包。在簡化的計算機網絡OSI模型中,它完成第四層傳輸層所

指定的功能,

TCP是一種面向連接配接的、可靠的、基于位元組流的運輸層通信協定。也就是說隻有建立了可靠的連

接才能進行通信,因為他們是結果三次“握手”的過程,是指先發送一次包,然後等待對方确認收

到并回複一次包,最後在開始發送資料包。

TCP協定就像生活中的打電話,A想要和B聯系,A就先撥通B的号碼,然後等待B接電話,如果B接

聽了就表示收到消息了,然後A才會開始和B說話,這就使用了TCP通信。

還有如視訊聊天、撥打網絡電話、固定電話等都是TCP通信,雙方都需要确認資料是否完全發送。

UDP (User Datagram Protocol)

是指使用者資料報協定,UDP是與TCP同一層内另一個重要的傳輸協定。它也跟IP協定一起使用,

但他不對連接配接狀态與資料丢失做檢查,隻保證資料發送出去,不管對方是否收到。

生活中常見的就是對講機,使用的是UDP協定,需要時直接說話,如果對方聽到就可以進行通信,

聽不到,你也就白說了。還有如平時我們用的QQ聊天,MSN,發郵件等,他們都可以離線發送消息,

不管對方是否收到。

Java對TCP和UDP協定提供了強有力的支援,分别引入了Socket類和DatagramSocket類,來解決

兩個不同協定的網絡程式設計。

3、端口與套接字

端口(Port)

這裡說的端口并非真實存在的,如果把IP位址比作一間房子,端口就是出入這間房子的門。

真正的房子隻有幾個門,但是一個IP位址的端口可以有65536(即2^16)個之多,端口是

通過端口号來标記的,端口号隻有整數,範圍是從0 到65535(2^16-1)。

其實計算機上的1-1023之間的端口都被系統占用了,是以在定義自己的端口時,不能使用這

一段端口号,應該使用1024-65535之間的任意端口,但也别使用其他軟體已經使用的端口号。

套接字(Socket)

套接字,是支援TCP/IP的網絡通信的基本操作單元,可以看作是不同主機之間的程序進行雙

向通信的端點,簡單的說就是通信的兩方的一種約定,用套接字中的相關函數來完成通信過程。

簡單的舉例說明:Socket = Ip address + TCP/UDP port(IP位址+端口号)。

在java API中将套接字抽象化稱為Socket類,是以程式隻需建立該類的對象,就能使用套接字。

那麼java就是使用Socket類的流對象進行資料傳輸的,該流分為輸入流和輸出流。

4、TCP與UDP的程式設計

TCP程式設計

java中的TCP網絡程式設計是利用 Socket 類編寫通信程式,設計TCP程式的過程是:伺服器的套

接字等待用戶端的連接配接請求,當伺服器接收到請求後就可以通過相應的方法擷取輸入流和輸出

流,進而實作相應的功能。

UDP程式設計

可利用DatagramSocket來編寫面向UDP協定的程式,步驟如下:

發送端程式編寫:

1)調用DatagramSocket()建立一個資料包套接字。

2)調用DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)建立發送的UDP包。

3)調用DatagramSocket類的send()方法發送UDP包。

4)關閉資料包套接字。

接收端程式編寫:

1)調用DatagramSocket(int port)建立一個資料包套接字,并綁定到指定端口。

2)調用DatagramPacket(byte[] buf,int length),建立一個位元組數組以接收UDP包。

3)調用DataSocket類的receive()接收UDP包。

4)關閉資料包套接字。

二、IP位址 InetAddress

InetAddress 此類表示網際網路協定 (IP) 位址,是與IP相關的類。

IP 位址是 IP 使用的 32 位或 128 位無符号數字,

它是一種低級協定,UDP 和 TCP 協定都是在它的基礎上建構的。

InetAddress 的執行個體包含 IP 位址,

還可能包含相應的主機名(取決于它是否用主機名構造或者是否已執行反向主機名解析)。

主機名解析

主機名到IP位址的解析:

通過使用本地機器配置資訊和網絡命名服務,如域名系統DNS和網絡資訊服務NIS來實作。

要使用的特定命名服務預設情況下是本地機器配置的那個,對于任何主機名稱,都傳回其相應的IP位址。

反向名稱解析:

意味着對于任何IP位址,都傳回與IP位址關聯的主機。

InetAddress緩存

InetAddress類具有一個緩存,用于存儲成功及不成功的主機名解析。

預設情況下,當為了防止 DNS 哄騙攻擊安裝了安全管理器時,正主機名解析的結果會永遠緩存。

當未安裝安全管理器時,預設行為将緩存一段有限(與實作相關)時間的條目。

不成功主機名解析的結果緩存非常短的時間(10 秒)以提高性能。

此類常用的方法:

getHostName() 擷取此IP位址的主機名。

getHostAddress() 傳回IP位址字元串(以文本表現形式)。

SgetCanonicalHostName()擷取此IP位址的完全限定域名

靜态方法:(傳回:static InetAddress)

getLocalHost() 傳回本地主機的InetAddress對象

getByName(String host) 在給定主機名的情況下擷取主機的IP位址

getByAddress(byte[] addr)在給定原始IP位址的情況下,傳回InetAddress對象 

getByAddress(String host, byte[] addr) 根據提供的主機名和IP位址建立InetAddress對象

需求;擷取指定主機資訊

執行個體1

import java.net.*;
class InetAddressDemo{
	public static void sop(Object obj){System.out.println(obj);}
	public static void main(String[] args) throws Exception{
		//擷取本地主機
		InetAddress i = InetAddress.getLocalHost();
		sop("本地主機:"+i);
		sop("完全限定域名:"+i.getCanonicalHostName());	//擷取此IP位址的完全限定域名
		sop("IP位址的哈希碼:"+i.hashCode());			//傳回此 IP 位址的哈希碼。
		sop("IP位址的主機名:"+i.getHostName());		//擷取此 IP 位址的主機名
		sop("IP位址的字元串:"+i.getHostAddress());		//傳回 IP 位址字元串(以文本表現形式)。
		
		//擷取網絡上指定主機的IP位址
		InetAddress ia = InetAddress.getByName("www.baidu.com");
		sop("\n正在擷取“www.baidu.com”的資訊");
		sop("完全限定域名:"+ia.getCanonicalHostName());	//擷取此IP位址的完全限定域名
		sop("IP位址的哈希碼:"+ia.hashCode());			//傳回此 IP 位址的哈希碼。
		sop("IP位址的主機名:"+ia.getHostName());		//擷取此 IP 位址的主機名
		sop("IP位址的字元串:"+ia.getHostAddress());	//傳回 IP 位址字元串(以文本表現形式)。	
		sop("是否能連接配接主機:"+ia.isReachable(5000));	//測試指定時間内(毫秒)是否可以達到該位址
		sop("将IP轉為字元串:"+ia.toString() );			//将此 IP 位址轉換為 String。
		sop(ia.isSiteLocalAddress());					//檢查 InetAddress 是否是站點本地位址的實用例行程式
	}
}
/*
結果(隻粘貼www.baidu.com部分的資訊)
正在擷取“www.baidu.com”的資訊
完全限定域名:115.239.210.27
IP位址的哈希碼:1945096731
IP位址的主機名:www.baidu.com
IP位址的字元串:115.239.210.27
是否能連接配接主機:false
将IP轉為字元串:www.baidu.com/115.239.210.27
false
*/
           

三、UDP程式設計

類 DatagramSocket

此類表示用來發送和接收資料報包的套接字。

資料報套接字是包投遞服務的發送或接收點。

每個在資料報套接字上發送或接收的包都是單獨編址和路由的。

從一台機器發送到另一台機器的多個包可能選擇不同的路由,也可能按不同的順序到達。

在DatagramSocket上總是啟用UDP廣播發送。

為了接收廣播包,應該将 DatagramSocket 綁定到通配符位址。

在某些實作中,将 DatagramSocket 綁定到一個更加具體的位址時廣播包也可以被接收。

構造方法:

DatagramSocket() 構造資料報套接字并将其綁定到本地主機上任何可用的端口。

DatagramSocket(int port) 建立資料報套接字并将其綁定到本地主機上的指定端口。

DatagramSocket(int port, InetAddress laddr) 建立資料報套接字,将其綁定到指定的本地位址。

DatagramSocket(SocketAddress bindaddr) 建立資料報套接字,将其綁定到指定的本地套接字位址。

構造方法中的參數:

port - 要使用的端口。

laddr - 要綁定的本地位址

bindaddr - 要綁定的本地套接字位址,對于未綁定的套接字為 null。

方法:

close() 關閉此資料報套接字。

getPort() 傳回此套接字的端口。

isBound() 傳回套接字的綁定狀态。

isClosed() 傳回是否關閉了套接字。

disconnect() 斷開套接字的連接配接。

isConnected() 傳回套接字的連接配接狀态。

getLocalPort() 傳回此套接字綁定的本地主機上的端口号。

getInetAddress() 傳回此套接字連接配接的位址。

send(DatagramPacket p) 從此套接字發送資料報包

getLocalSocketAddress() 傳回此套接字綁定的端點的位址,如果尚未綁定則傳回 null。

getRemoteSocketAddress() 傳回此套接字連接配接的端點的位址,如果未連接配接則傳回 null。

receive(DatagramPacket p) 從此套接字接收資料報包。

connect(SocketAddress addr) 将此套接字連接配接到遠端套接字位址(IP 位址 + 端口号)。

connect(InetAddress address, int port) 将套接字連接配接到此套接字的遠端位址。

------------------------------------------------------------------------------

類 DatagramPacket

此類表示資料報包。

資料報包用來實作無連接配接包投遞服務。每條封包僅根據該包中包含的資訊從一台機器路由到另一台機器。

從一台機器發送到另一台機器的多個包可能選擇不同的路由,也可能按不同的順序到達。不對包投遞做出保證。

構造方法:

DatagramPacket(byte[] buf, int length)

    構造 DatagramPacket,用來接收長度為 length 的資料包。

DatagramPacket(byte[] buf, int offset, int length)

    構造 DatagramPacket,用來接收長度為 length 的包,在緩沖區中指定了偏移量。

DatagramPacket(byte[] buf, int length, SocketAddress address)

    構造資料報包,用來将長度為 length 的包發送到指定主機上的指定端口号。

DatagramPacket(byte[] buf, int length, InetAddress address, int port)

    構造資料報包,用來将長度為 length 的包發送到指定主機上的指定端口号。

方法:

getPort() 傳回某台遠端主機的端口号。int 

getData() 傳回資料緩沖區。byte[] 

getLength() 傳回将要發送或接收到的資料的長度。int 

getOffset() 傳回将要發送或接收到的資料的偏移量。int 

getAddress() 傳回某台機器的IP位址。InetAddress 

getSocketAddress() 擷取要将此包發送到的或發出此資料報的遠端主機的SocketAddress。

setData(byte[] b) 為此包設定資料緩沖區。

setLength(int len) 為此包設定長度。

setPort(int iport) 設定要将此資料報發往的遠端主機上的端口号。

setAddress(InetAddress iaddr) 設定要将此資料報發往的那台機器的 IP 位址。

setData(byte[] buf,int off,int len) 為此包設定資料緩沖區。

setSocketAddress(SocketAddress address) 設定要将此資料報發往的遠端主機的SocketAddress。

1、UDP發送端

需求:通過UDP傳輸方式,将一段文字發送出去。

思路:

1)建立udpsocket服務。

2)提供資料,并将資料封裝到資料包中。

3)通過socket服務的發送功能,将資料包發送出去。

3)關閉資源。

構造函數:

DatagramPacket(byte[] buf, int length, InetAddress address, int port)

建立資料包,将指定長度的包發送到指定主機上的指定端口号。

執行個體2

import java.net.*;
class UdpSend{
	public static void main(String[] args) throws Exception{
		//建立UDP套接字綁定指定端口。不指定将随機配置設定。
		DatagramSocket ds = new DatagramSocket(8888);
		//建立數組緩沖區,并指定資料
		byte[] buf = "hello,你好".getBytes();
		//建立資料包對象--把緩沖區中的資料發送到指定主機的指定端口号。
		DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.100"),10000);		
		//通過套接字的send方法把資料包發送出去,然後關閉套接字。
		ds.send(dp);
		System.out.println("發送成功");//測試		
		ds.close();
	}
}
           

2、UDP接收端

需求:接收udp協定傳輸的資料并處理。

思路:

1、定義UDP的 socket服務。監聽一個端口,其實就是給這個接收網絡應用程式定義數字辨別,

 友善明确使用什麼應用程式來處理資料。

2、定義一個資料包,因為要存儲接收到的位元組資料。

3、通過socket服務的receive方法将接收到的資料存入已定義好的資料包中。

4、通過資料包對象的特有功能,将這些不同的資料提取出來。列印在控制台。

5、關閉資源。

通過String類的構造方法來擷取資料包中的資料:

 String(byte[] bytes, int offset, int length)

 通過使用平台的預設字元集解碼指定的 byte 子數組,構造一個新的 String。

執行個體3

import java.net.*;
class UdpRece{
	public static void main(String[] args) throws Exception{
		//建立套接字,綁定到指定端口
		DatagramSocket ds = new DatagramSocket(10000);
		while(true){
			//建立數組緩沖區
			byte[] buf = new byte[1024]	;
			//建立資料包,接收緩沖區中的全部資料
			DatagramPacket dp = new DatagramPacket(buf,buf.length);		
			//通過套接字ds的receive方法将收到的資料存入資料包dp中。阻塞式方法。
			ds.receive(dp);	
			//擷取資料包中的IP位址、資料、端口号
			String ip = dp.getAddress().getHostAddress();
			String data = new String(dp.getData(),0,dp.getLength());
			int port = dp.getPort();
			//列印到控制台
			System.out.println("發送者IP:"+ip);
			System.out.println("發送者端口:"+port);
			System.out.println("發送的内容:"+data);
		}
		//ds.close();//不能在while循環外部關閉
	}
}
           

3、UDP通信(用鍵盤錄入方式傳輸資料)

執行個體4

import java.io.*;
import java.net.*;
//UDP發送端
class UDPSend2{
	public static void main(String[] args) throws Exception{	
		//建立資料報套接字,預設綁定到任何可用的端口
		DatagramSocket ds = new DatagramSocket();		
		//将鍵盤錄入的位元組轉換成字元,然後存入字元流緩沖區。
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		//循環讀取字元緩沖區br中的一行資料
		String line = null;
		while((line=br.readLine())!=null){
			//如果遇到"886",就跳出循環
			if("886".equals(line))						
				break;
			//定義緩沖區,存儲讀到的資料
			byte[] buf = line.getBytes();
			//建立資料報包,将緩沖區中的全部資料發送到指定主機上的指定端口号
			DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.100"),10001);
			//從此套接字發送資料報包
			ds.send(dp);
		}
		ds.close();
	}
}
//UDP接收端
class UDPRece2{
	public static void main(String[] args) throws Exception{
		//建立資料報套接字并綁定到本主機上的指定端口
		DatagramSocket ds = new DatagramSocket(10001);
		while(true){
			//建立緩沖區
			byte[] buf = new byte[1024];
			//建立資料包,接收緩沖區中的全部
			DatagramPacket dp = new DatagramPacket(buf,buf.length);
			//從套接字接收資料報包
			ds.receive(dp);
			//從資料包中擷取ip位址和資料
			String ip = dp.getAddress().getHostAddress();
			String data = new String(dp.getData(),0,dp.getLength());
			System.out.println(ip+":"+data);
		}
	}
}
           

4、UDP聊天

需求:用UDP方式編寫一個聊天程式,需要發送端和接收端同時執行。

分析:

同時執行就要用到多線程技術,一個線程控制收,一個線程控制發。

因為收和發動作是不一緻的,是以要定義兩個run方法。

而且這兩個方法要封裝到不同的類中。

執行個體5

import java.io.*;
import java.net.*;
//發送端
class SendSocket implements Runnable{
	private DatagramSocket ds;
	public SendSocket(DatagramSocket ds){
		this.ds = ds;
	}
	//設定發送端線程run方法
	public void run(){
		try{
			//将鍵盤錄入的資料存入字元流緩沖區
			BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
			String line = null;
			//讀取緩沖區中一行資料
			while((line=bufr.readLine())!=null){
				//設定跳出循環标記
				if("886".equals(line))
					break;
				//将讀到的資料儲存到
				byte[] b = line.getBytes();
				//将緩沖區中資料發送懂指定主機的指定端口上。
				DatagramPacket dp =
					new DatagramPacket(b,b.length,InetAddress.getByName("192.168.0.100"),10001);
				//将資料包發送到套接字中
				ds.send(dp);
			}
			ds.close();
		}
		catch (Exception e){
			throw new RuntimeException("發送失敗");
		}
	}
}
//接收端
class ReceSocket implements Runnable{
	private DatagramSocket ds;
	//構造資料報套接字
	public ReceSocket(DatagramSocket ds){
		this.ds = ds;
	}
	//設定接收端線程的run方法
	public void run(){
		try{
			while(true){
				//建立位元組數組緩沖區
				byte[] by = new byte[1024];	
				//建立資料包對象--接收緩沖區全部資料
				DatagramPacket dp = new DatagramPacket(by,by.length);
				//把套接字中接收到的資料存入資料包
				ds.receive(dp);
				//擷取資料包中的ip位址
				String ip = dp.getAddress().getHostAddress();
				//擷取資料包喊緩沖區中的資料
				String data = new String(dp.getData(),0,dp.getLength());
				System.out.println(ip+":"+data);
			}
		}
		catch (Exception e){
			throw new RuntimeException("接收失敗");
		}
	}
}
class SocketDemo {
	public static void main(String[] args) throws Exception{
		//建立發送和接收服務的對象
		DatagramSocket sendSocket = new DatagramSocket();
		DatagramSocket receSocket = new DatagramSocket(10001);
		//建立兩個線程,同時執行
		new Thread(new SendSocket(sendSocket)).start();
		new Thread(new ReceSocket(receSocket)).start();
	}
}
           

四、TCP程式設計

類 ServerSocket

伺服器套接字

伺服器套接字等待請求通過網絡傳入。它基于該請求執行某些操作,然後可能向請求者傳回結果。

伺服器套接字的實際工作由 SocketImpl 類的執行個體執行。應用程式可以更改建立套接字實作的套接

字工廠來配置它自身,進而建立适合本地防火牆的套接字。

構造方法:

ServerSocket()

   建立非綁定伺服器套接字。

ServerSocket(int port)

   建立綁定到特定端口的伺服器套接字。

ServerSocket(int port, int backlog)

   利用指定的 backlog 建立伺服器套接字并将其綁定到指定的本地端口号。

ServerSocket(int port, int backlog, InetAddress bindAddr)

   使用指定的端口、偵聽 backlog 和要綁定到的本地 IP 位址建立伺服器。

構造方法參數:

    port - 本地 TCP 端口

 backlog - 偵聽 backlog

bindAddr - 要将伺服器綁定到的 InetAddress

方法:

 void close() 關閉此套接字。

Socket accept() 偵聽并接受到此套接字的連接配接。

boolean isBound() 傳回ServerSocket的綁定狀态。

boolean isClosed() 傳回ServerSocket的關閉狀态。

String toString() 作為String傳回此套接字的實作位址和實作端口。 

int getLocalPort() 傳回此套接字在其上偵聽的端口。

InetAddress getInetAddress() 傳回此伺服器套接字的本地位址。

SocketAddress getLocalSocketAddress() 傳回此套接字綁定的端點的位址,如果尚未綁定則傳回 null。 

void bind(SocketAddress endpoint) 将ServerSocket綁定到特定位址(IP 位址和端口号)。 

void bind(SocketAddress endpoint, int backlog) 将ServerSocket綁定到特定位址(IP 位址和端口号)。

方法中的參數:

endpoint - 要綁定的 IP 位址和端口号。

backlog - 偵聽 backlog 長度。

類 Socket

此類實作用戶端套接字(也可以就叫“套接字”)。

套接字是兩台機器間通信的端點。 套接字的實際工作由 SocketImpl 類的執行個體執行。

應用程式通過更改建立套接字實作的套接字工廠可以配置它自身,以建立适合本地防火牆的套接字。

構造方法:

Socket() 通過系統預設類型的 SocketImpl 建立未連接配接套接字

Socket(InetAddress address, int port)

   建立一個流套接字并将其連接配接到指定 IP 位址的指定端口号。

Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

   建立一個套接字并将其連接配接到指定遠端位址上的指定遠端端口。 

Socket(String host, int port)

   建立一個流套接字并将其連接配接到指定主機上的指定端口号。 

Socket(String host, int port, InetAddress localAddr, int localPort)

   建立一個套接字并将其連接配接到指定遠端主機上的指定遠端端口

構造方法參數:

     port - 端口号。

  address - IP 位址。

localAddr - 要将套接字綁定到的本地位址

localPort - 要将套接字綁定到的本地端口

     host - 主機名,或者為 null,表示回送位址。

方法:

        void close() 關閉此套接字。

     boolean isBound() 傳回套接字的綁定狀态。

     boolean isClosed() 傳回套接字的關閉狀态。

      String toString() 将此套接字轉換為 String。

         int getPort() 傳回此套接字連接配接到的遠端端口。

         int getLocalPort() 傳回此套接字綁定到的本地端口。

        void shutdownOutput() 禁用此套接字的輸出流。

 InetAddress getInetAddress() 傳回套接字連接配接的位址。

 InputStream getInputStream() 傳回此套接字的輸入流。

OutputStream getOutputStream() 傳回此套接字的輸出流。

void connect(SocketAddress endpoint) 将此套接字連接配接到伺服器。

------------------------------------------------------------------------------

1、大寫字母轉換伺服器

需求:

用戶端給服務端發送文本資料(字母),服務端将資料轉換成大寫字母傳回給用戶端。

而且用戶端可以不斷的進行文本轉換,當用戶端輸入結束标記如"over"時,關閉服務端。

分析:

用戶端:既然是操作裝置上的資料,那麼就可以使用IO技術,并按照IO的操作規律來思考。

源:鍵盤錄入。

目标:網絡裝置,即網絡輸出流。

操作的是文本資料,可以使用字元流。

步驟:

1)建立Socket服務。

2)擷取鍵盤錄入的資料。

3)将資料發送給服務端。

4)擷取服務端傳回的大寫資料。

5)關閉資源。

都是文本資料,可以使用字元流進行操作,為提高效率,加入緩沖。

用戶端和服務端都使用阻塞式方法。

服務端:

源:Socket讀取流。

目标:Socket輸出流。

執行個體6

import java.io.*;
import java.net.*;
//用戶端
class TransClient{
	public static void main(String[] args) throws Exception { 
		//建立用戶端套接字,綁定到指定主機的指定端口。
		Socket s = new Socket("192.168.0.100",10005);

		//從鍵盤錄入資料,轉換為字元流,存入字元讀取流緩沖區bufr。
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		
		//BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		
		//直接使用列印流接收套接字中的輸出流。true:println方法将重新整理輸出緩沖區
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		
		//擷取套接字中的輸入流,轉換成字元流,存入字元讀取流緩沖區bufIn。
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		//從字元讀取流緩沖區bufIn讀取一行資料
		while((line=bufr.readLine())!=null){
			if("over".equals(line))	
				break;
			//bufOut.write(line);				//把資料發給服務端。
			//bufOut.newLine();					//添加行結束标記
			//bufOut.flush();					//重新整理緩沖區
			//使用列印流技術就省了以上三句。
			//用列印流的println方法把讀到的資料列印到字元列印流中。此方法自動換行自動重新整理緩沖區。
			out.println(line);
			//擷取緩沖區bufIn中的一行資料,列印到控制台。
			System.out.println("伺服器傳回的資料:"+bufIn.readLine());
		}
		bufr.close();
		s.close();
	}
}
//服務端
class TransServer{
	public static void main(String[] args) throws Exception {
		//建立伺服器套接字,綁定到指定端口。
		ServerSocket ss = new ServerSocket(10005);
		
		//偵聽并接受到此套接字的連接配接。傳回新套接字s,accept方法在接收到連接配接之前一直阻塞。
		Socket s = ss.accept();
		
		//從套接字中擷取連接配接進來的IP
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("IP="+ip);//測試

		//擷取套接字輸入流中的資料,轉換成字元流,存到字元讀取流緩沖區。
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		
		//把資料寫入套接字輸出流,存到寫入流緩沖區。
		//BufferedWriter bufOut= new BufferedReader(new OutputStreamWriter(s.getOutputStream()));
		
		//用列印流技術替換上一句,位元組流字元流都能接收,而且自動重新整理。
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);		
		String line = null;
		//從字元讀取流緩沖區bufIn讀取一行資料
		while((line=bufIn.readLine())!=null){
			//把讀到的資料轉換成大寫列印到字元列印流中。就是發給用戶端
			out.println(line.toUpperCase());
		}
		s.close();
		ss.close();
	}
}
           

2、TCP上傳檔案

執行個體7

import java.io.*;
import java.net.*;
//用戶端
class UpLoadText{
	public static void main(String[] args) throws Exception {
		Socket s = new Socket("192.168.0.100",10010);
		BufferedReader bufr = new BufferedReader(new FileReader("NoteBook.java"));
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		
		String line = null;
		while((line=bufr.readLine())!=null){
			out.println(line);
		}
		s.shutdownOutput();//禁用用戶端的輸出流,相當于給流中加入結束标記-1。
		
		//建立輸入流,擷取服務端的回報資訊,列印到控制台
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String str = bufIn.readLine();
		System.out.println(str);
		bufr.close();
		s.close();
	}
}
//服務端
class TextServer{
	public static void main(String[] args) throws Exception {
		ServerSocket ss = new ServerSocket(10010);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("來自IP:"+ip);
		
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		PrintWriter out = new PrintWriter(new FileWriter("NoteBook_upload.java"),true);
		
		String line = null;
		while((line=bufIn.readLine())!=null){
			if("over".equals(line))
				break;
			out.println(line);
		}
		PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
		pw.println("上傳成功");//給用戶端回報資訊。
		out.close();
		s.close();
		ss.close();
	}
}
           

3、TCP上傳圖檔到伺服器

流程分析:

1)建立服務端點

2)讀取用戶端已有的圖檔資料

3)通過Socket輸出流将資料發給服務端

4)讀取服務端回報資訊

5)關閉資源

執行個體8

import java.io.*;
import java.net.*;
class PicClient{
	public static void main(String[] args) throws Exception{
		Socket s = new Socket("192.168.0.100",10011);
		FileInputStream fis = new FileInputStream("F:\\laola.bmp");
		OutputStream out = s.getOutputStream();
		byte[] buf = new byte[1024];
		int len = 0;
		while((len=fis.read(buf))!=-1){
			out.write(buf,0,len);
		}
		s.shutdownOutput();//結束标志
		InputStream in = s.getInputStream();
		byte[] bufIn = new byte[1024];
		int num = in.read(bufIn);
		System.out.println(new String(bufIn,0,num));
		fis.close();
		s.close();
	}
}
//服務端
class PicServer{
	public static void main(String[] args) throws Exception{
		ServerSocket ss = new ServerSocket(10011);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("來自IP:"+ip);

		InputStream in = s.getInputStream();
		FileOutputStream fos = new FileOutputStream("laola_Demo.bmp");
		byte[] buf = new byte[1024];
		int len = 0;
		while((len=in.read(buf))!=-1){
			fos.write(buf,0,len);
		}
		OutputStream out = s.getOutputStream();
		out.write("上傳成功".getBytes());
		fos.close();
		s.close();
		ss.close();
	}	
}
           

4、優化TCP上傳圖檔

以上這個服務端有個局限性,當A客戶連接配接上以後,被服務端擷取到,服務端執行具體流程,

這時候B客戶連接配接,隻有等待,因為服務端還沒有處理完A客戶的請求,還要循環回來執行下

一次accept方法,是以暫時擷取不到B客戶對象。

為了可以讓多個用戶端同時冰法通路服務端,那麼服務端最好就是将每個用戶端封裝到一個

單獨的線程中,這樣就可以同時處理多個用戶端請求。

如何定義線程呢?

隻要明确每個用戶端要在服務端執行的代碼即可,将該代碼存入run方法中。

執行個體9

import java.io.*;
import java.net.*;
class PicClient{
	public static void main(String[] args) throws Exception{
		//測試:用利用主函數傳遞檔案名稱
		if(args.length!=1){
			System.out.println("請選擇一個圖檔檔案");
			return ;
		}
		//判斷是否是檔案或是否存在
		File file = new File(args[0]);
		if(!(file.exists()&&file.isFile())){
			System.out.println("該檔案不存在或不是檔案");
			return ;
		}
		//判斷檔案名不不是.bmp格式
		if(!file.getName().endsWith(".bmp") && !file.getName().endsWith(".jpg")){
			System.out.println("圖檔格式隻支援jpg/bmp");
			return ;
		}
		//判斷檔案大小
		if(file.length()>1024*1024*10){
			System.out.println("檔案太大,必須小于10M");
			return ;
		}
		//以上判斷都滿足,就建立ServerSocket對象
		Socket s = new Socket("192.168.0.100",10012);
		FileInputStream fis = new FileInputStream("F:\\laola.bmp");
		OutputStream out = s.getOutputStream();
		byte[] buf = new byte[1024];
		int len = 0;
		while((len=fis.read(buf))!=-1){
			out.write(buf,0,len);
		}
		s.shutdownOutput();//結束标志
		InputStream in = s.getInputStream();
		byte[] bufIn = new byte[1024];
		int num = in.read(bufIn);
		System.out.println(new String(bufIn,0,num));
		fis.close();
		s.close();
	}
}

//服務端
class PicThread implements Runnable{
	private Socket s;
	PicThread(Socket s){
		this.s = s;
	}
	public static void main(String[] args) throws Exception{	
		ServerSocket ss = new ServerSocket(10012);
		//循環判斷,連進來一個用戶端就建立一個線程
		while(true){
			Socket s = ss.accept();
			new Thread(new PicThread(s)).start();
		}
		//伺服器一般是不關閉的
		//ss.close();
	}
	public void run(){
		//定義局部變量
		int count = 1;
		String ip = s.getInetAddress().getHostAddress();
		try{
			System.out.println(ip+"請求連接配接");
			InputStream in = s.getInputStream();
			//定義上傳後的圖檔名稱
			File file = new File(ip+"("+count+").jpg");
			while(file.exists())
				file = new File(ip+"("+(count++)+").jpg");
			FileOutputStream fos = new FileOutputStream(file);
			byte[] buf = new byte[1024];
			int len = 0;
			while((len=in.read(buf))!=-1){
				fos.write(buf,0,len);
			}
			OutputStream out = s.getOutputStream();
			out.write("上傳成功".getBytes());
			fos.close();
			s.close();
		}
		catch (Exception e){
			throw new RuntimeException(ip+" 上傳失敗");
		}
	}	
}
           

5、TCP用戶端并發登入

用戶端通過鍵盤錄入使用者名,服務端對這個使用者名進行效驗

如果該使用者存在,在服務端顯示 XXX已登入,并在用戶端顯示 xxx歡迎光臨。

如果該使用者不存在,在服務端顯示 XXX,嘗試登入,并在用戶端顯示 xxx該使用者不存在。

最多登入三次就不給登入了。

執行個體10

import java.io.*;
import java.net.*;
class LoginClient{
	public static void main(String[] args) throws Exception{
		Socket s = new Socket("192.168.0.100",10013);
		//讀取鍵盤錄入的資料
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);	
		//讀取伺服器傳回的資訊
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		for(int x=0;x<3;x++){
			//如果讀到空,就跳出循環。
			String line = bufr.readLine();
			if(line==null)
				break ;
			out.println(line);
			String info = bufIn.readLine();
			System.out.println(info);
			if(info.contains("0"))
				break ;
			//如果服務端傳回資訊中包含”歡迎“就表示登入成功,結束登入循環次數
			if(info.contains("歡迎"))
				break ;
		}
		bufr.close();
		s.close();
	}
}

//服務端
class UserThread implements Runnable{
	private Socket s;
	UserThread(Socket s){
		this.s = s;
	}
	public void run(){
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+" 已連接配接進來");
		try{
			for(int x=0;x<3;x++){
				//讀取用戶端發過來的使用者名
				BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
				String name = bufIn.readLine();
				BufferedReader bufr = new BufferedReader(new FileReader("UserInfo.txt"));
				PrintWriter out = new PrintWriter(s.getOutputStream(),true);
				String line = null;

				//定義一個是否存在的标記
				boolean flag = false;
				//從資料庫檔案中一行一行讀取,進行判斷
				while((line=bufr.readLine())!=null){
					//如果該name存在資料庫中
					if(line.equals(name)){
						//标記為true,表示存在就登入成功
						flag = true;
						break ;
					}
				}
				//如果使用者名存在
				if(flag){
					//在服務端發送提示
					System.out.println(name+" 已登入");
					//在用戶端發送提示
					out.println(name+" 歡迎光臨");
					break ;
				}
				//不存在
				else{
					//在服務端發送提示
					System.out.println(name+" 正在嘗試登入");
					//在用戶端發送提示
					out.println(name+"使用者名不存在 ");
				}
			}
			//關閉用戶端連接配接
			s.close();
		}
		catch (Exception e){
			throw new RuntimeException(ip+"校驗失敗");
		}
	}

}
class LoginServer{
	public static void main(String[] args) throws Exception{	
		ServerSocket ss = new ServerSocket(10013);
		while(true){
			Socket s = ss.accept();
			new Thread(new UserThread(s)).start();
		}
	}
}
           

6、一個簡單的伺服器

執行個體11

import java.io.*;
import java.net.*;
class Server{
	public static void main(String[] args) throws Exception{
		ServerSocket ss = new ServerSocket(11000);
		Socket s = ss.accept();
		//擷取用戶端的IP
		System.out.println(s.getInetAddress().getHostAddress());
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		//向用戶端發送資料
		out.println("<font color='red' size='7'>用戶端你好</font>");
		s.close();
		ss.close();
	}	
}
           

五、URL (URLConnection)

URL(Uniform Resource Locator)的縮寫。

URL類代表一個統一資源定位符,被稱為網頁位址,它是指向網際網路“資源”的指針。

資源可以是簡單的檔案或目錄,也可以是對更為複雜的對象的引用,例如對資料庫或搜尋引擎的查詢。

通常URL可分成幾個部分:

示例:http://www.wgxin.com:80/index.shtml

上面的URL示例訓示使用的協定為 http(超文本傳輸協定)

并且該資訊駐留在一台名為 www.wgxin.com 的主機上。

主機上的資訊名稱為:index.shtml

主機上此名稱的準确含義取決于協定和主機。

該資訊一般存儲在檔案中,但可以随時生成,該URL的這一部分稱為路徑。

URL 可選擇指定一個“端口”,它是用于建立到遠端主機 TCP 連接配接的端口号。

如果未指定該端口号,則使用協定預設的端口。例如,http 協定的預設端口為 80。

還可以指定一個備用端口,如下所示:

http://www.wgxin.com:80/index.shtml

構造方法:

根據 String 表示形式建立 URL 對象。

URL(String spec)

根據指定 protocol、host、port 号和 file 建立 URL 對象。          

URL(String protocol, String host, int port, String file)

根據指定的 protocol、host、port 号、file 和 handler 建立 URL 對象。      

URL(String protocol, String host, int port, String file, URLStreamHandler handler)

根據指定的 protocol 名稱、host 名稱和 file 名稱建立 URL。    

URL(String protocol, String host, String file)

通過在指定的上下文中對給定的 spec 進行解析建立 URL。          

URL(URL context, String spec)

通過在指定的上下文中用指定的處理程式對給定的 spec 進行解析來建立 URL。          

URL(URL context, String spec, URLStreamHandler handler)

構造方法參數:

spec - 将作為URL解析的String。

host - 主機名稱。

port - 主機端口号。

file - 主機上的檔案

handler - URL 的流處理程式。

context - 要在其中解析規範的上下文。

protocol - 要使用的協定名稱。

方法:

 String getRef()  擷取此 URL 的錨點(也稱為“引用”)。

 String getFile() 擷取此 URL 的檔案名。

 String getHost() 擷取此 URL 的主機名(如果适用)。

 String getPath() 擷取此 URL 的路徑部分。

    int getPort() 擷取此 URL 的端口号。

    int hashCode() 建立一個适合哈希表索引的整數。

 String getQuery() 擷取此 URL 的查詢部分。

 Object getContent() 擷取此 URL 的内容。

 String getUserInfo() 擷取此 URL 的 userInfo 部分。

 String getProtocol() 擷取此 URL 的協定名稱。

 String getAuthority() 擷取此 URL 的授權部分。

    int getDefaultPort() 擷取與此 URL 關聯協定的預設端口号。 

 Object getContent(Class[] classes) 擷取此 URL 的内容。

boolean equals(Object obj) 比較此 URL 是否等于另一個對象。

需求:擷取URL的各個參數

執行個體12

import java.net.*;
class URLDemo{	
	public static void sop(Object obj){
		System.out.println(obj);
	}
	public static void main(String[] args) throws Exception{
		//該URL ?後面的參數是加上去的	
		URL url = new URL("http://www.wgxin.com/index.shtml?name=zhangsan&age=20#30");
		sop("URL授權部分:"+url.getAuthority());
		sop("URL的協定:"+url.getProtocol());
		sop("URL的IP位址:"+url.getHost());
		sop("URL端口号:"+url.getPort());
		sop("URL檔案路徑:"+url.getPath());
		sop("URL檔案名稱:"+url.getFile());	
		sop("URL查詢部分:"+url.getQuery());
		sop("URL的錨點:"+url.getRef());
		sop("與URL關聯協定的預設端口号:"+url.getDefaultPort());
		sop("userInfo部分:"+url.getUserInfo());
		sop("構造此URL的字元串表示形式:"+url.toExternalForm());
		sop("傳回與此URL等效的URI:"+url.toURI());
		//sop("URL的内容:"+url.getContent()); //這是無效的URL,是以使用該方法會報錯
	}
}
/*
運作結果:
URL授權部分:www.wgxin.com
URL的協定:http
URL的IP位址:www.wgxin.com
<pre class="java" name="code">URL端口号:-1
           

版權聲明:本文為CSDN部落客「weixin_33881140」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/weixin_33881140/article/details/91834526