1.基本網絡概念html
1.1網絡java
1.1.1網絡是相互發送和接受數據的計算機和其餘設備的集合node
每個設備就是一個網絡節點(node)web
每個計算機是一個主機(host)算法
1.1.2每一個網絡節點有地址設計模式
以太網分配物理地址數組
Internet服務器分配Internet地址緩存
地址能夠有標識名字,可是不與地址鎖定安全
1.1.3數據交換使用包交換服務器
包有發送人和目標地址信息
能夠互不干擾的共用線纜
能夠進行安全校驗
須要提供協議定義數據傳遞規則
1.1.4網絡的分層
網絡協議棧
TCP/IP四層模型
Java網絡類只工做在應用層和傳輸層上
主機網絡層,鏈接硬件
網際層,用於路由和尋址,支持不一樣類型的主機網絡層相互對話,包含IPv4和IPv6兩個協議,如下是IPv4數據報結構
傳輸層,負責確保包以發送的順序接受,保證數據沒有丟失或破壞,包含對丟失或破壞的數據進行有序重發的TCP(傳輸控制協議)協議和檢測破壞包、但不保證有序的UDP(數據報協議)協議
應用層,向用戶傳輸數據的層,用於Web的HTTP、用於電子郵件的SMTP、POP和IMAP、用於文件傳輸的FTP、FASP和TFTP、用於文件訪問的NFS、用於文件共享的Gnutella和BitTorrent、用於語音通訊的回話啓動協議SIP和Skype
1.2IP、TCP和UDP
1.3.1IP
容許任意兩點之間有多個路由
TCP確認鏈接兩端收到IP包
Java在傳輸層只支持TCP和UDP
1.3.2IP地址
IPv4地址,4字節地址,例如192.168.6.190
包的首部保護目的地址和源地址
IPv4地址已經用完,目前在向IPv6地址過渡
IPv6地址,16字節地址,例如FEDC:7654:3210:FEDC:BA98:7654:3210
1.3.3域名(DNS)
用於記憶主機名,並在使用時轉換爲ip地址
IP地址會隨着時間變換
127.0.0.1表示本地回送地址
IPv6的本地回送地址爲::1
1.3.4端口
每臺計算機有65535個端口
1到1023保留給已知服務
已知端口分配
1.3Internet
1.3.1是一個給予IP的網絡
1.3.2Internet地址分塊
由ISP分配IPv4地址
固定了前24位,稱爲/24
最低地址表示網絡自己
最高地址是這個網絡的廣播地址
1.3.3網絡地址轉換
因爲外網地址不夠用,須要使用NAT網絡地址轉換,會致使外網地址和內網地址不一樣
1.3.4防火牆
惟一Internet和本地網絡直接的硬件和軟件會檢查因此進出的數據,保證數據合法性,稱爲防火牆
1.3.5代理服務器
外部請求經過代理服務器想內部機器進行請求,外部主機只能看到代理服務器,沒法知道內部機器的主機名和IP地址,保護內部機器
能夠控制訪問狀況
能夠實現本地緩存
可是沒法應對全部協議,能夠經過HTTP介入自定義協議,例如SOAP
1.4客戶/服務器模型
1.4.1服務端發送數據,客戶端接收數據
1.4.2對等端經過服務器程序實現相互通訊
1.5Internet標準
1.5.1IETF標準包括TCP/IP、MIME和SMTP
1.5.2W3C標準包括HTTP、HTML和XML
2.流
2.1輸出流
2.1.1write(int b)一次寫入1字節,開銷很大
2.1.2write(byte[] data)或write(byte[] data,int offset,int length)比一次寫入一字節快得多
2.1.3flush()強迫緩衝的流發送數據,應當在關閉流前刷新輸出
2.1.4close()關閉流,java7帶資源的try
try(OutputStream out = new FileOutputStream("/a.txt")){ //處理輸出流... }catch(IOException ex){ System.err.println(ex.getMessage); }
2.2輸入流
2.2.1read()一次讀取1字節
2.2.2read(byte[] input)或read(byte[] input,int offset,int length)嘗試讀取指定數組的input
後續字節未到達而失敗,能夠讀取知道數組填滿
後續字節已丟失而失敗,先判斷是否讀完
int bytesRead = 0; int byteToRead = 1024; byte[] input = new byte[byteToRead ]; while(true){ int result = in.read(input,bytesRead ,bytesToRead-bytesRead ); if(result == -1){break;} bytesRead += result; }
2.2.3available()肯定不阻塞的狀況下有多少字節能夠讀取
2.2.4skip()跳過數據不進行讀取
2.2.5標記和重置
mark(int readAheadLimit)標記流的當前位置,參數爲能夠重置的字節數限制大小,只能有一個標記,已有標記會被新標記替換
reset()把流重置到以前標記的位置
markSupported()檢查這個流是否支持標記和重置
2.3過濾器流
2.3.1過濾器讀取過程
2.3.2將過來串鏈在在一塊兒
過濾器經過構造函數與流鏈接,爲了不使用到前面的流的read方法,須要有意的重寫底層輸入流的引用
InputStream in = new InputStream("a.txt"); in = new BufferedInputStream(in);
2.3.3緩衝流
public BufferedInputStream(InputStream in, int size)
參數1爲底層流,向其讀出或寫入未緩衝的數據
參數2爲緩衝區的字節數,輸入緩存默認爲2048字節,輸出緩存默認爲512字節
2.3.4PrintStream過濾器輸出流,是有害的
輸出與平臺有關
使用平臺默認編碼方式
吞掉了全部異常
2.3.5數據流DataInputStream
用二進制格式讀/寫Java基本數據類型和字符串
2.4閱讀器和書寫器
2.4.1書寫器
OutputStreamWriter構造函數知道了要寫入的輸出流和編碼方式
2.4.2閱讀器
InputStreamReader根據知道編碼將字節轉換爲字符
2.4.3過濾器閱讀器和書寫器
OutputStreamWriter和InputStreamReader至關於輸入和輸出流上的裝飾器,把面向字節的接口改成面向字符的接口
2.4.4PrintWriter
正確處理多字節字符集和國際化文本
3.線程
3.1運行線程
3.1.1進程->進程池->線程->線程池->NIO
3.1.2把線程要作的工做放在run方法中
3.1.3派生Tread
派生子類,覆蓋run方法
3.1.4實現Runnable接口
實現Runnable接口來避免覆蓋表中Tread方法
3.2從線程返回信息
3.2.1競態條件
獲得的結果是正確仍是一次取決於線程個數,cup和磁盤的速度,系統的cpu個數以及不一樣線程分配的時間,這些稱爲競態條件
3.2.2輪詢
設置標誌值,直到這個值改變,表明線程結束,使用返回值
3.2.3回調
當線程結束時,告訴主程序線程結束
線程結束時調用回調對象的實例方法傳遞結果
避免在構造函數中啓動線程
和輪詢相比不會浪費cpu並且更靈活
對實例感興趣的能夠添加到回調的對象列表來完成註冊,例如事件的監聽,又叫觀察者模式
3.2.4Future、Callable和Executor
建立ExecutorService,會爲你建立線程
提交一個Callable任務,獲得一個Future
向Future請求線程任務結果,若是有就獲得,沒有就等待
public class test { public static void main(String[] args) throws ExecutionException, InterruptedException { //任務分解 MyTask myTask1 = new MyTask(); MyTask myTask2 = new MyTask(); //建立2個線程 ExecutorService service = Executors.newFixedThreadPool(2); Future<Integer> future1 = service.submit(myTask1); Future<Integer> future2 = service.submit(myTask2); System.out.println(Math.max(future1.get(), future2.get())); } } class MyTask implements Callable { @Override public Object call() throws Exception { //線程任務 return null; } }
3.3同步
3.3.1線程經過共享內容、文件句柄、socket和其餘資源使得程序更高效,可是須要保證線程不一樣時訪問同一個資源
3.3.2同步塊
將程序包在synchronized塊中,能防止對同一個對象同步的其餘線程使用這個共享資源
只要有多個線程共享資源,必須考慮同步
致使衝突的能夠是一個靜態類變量,也能夠是實例對象
3.3.3同步方法
經過synchronized修飾符修飾方法
會下降代碼速度
增長死鎖可能性
若是隻是類的實例須要同步,可能不能保護真正須要保護的對象
3.3.4同步的替代方法
使用局部變量替代字段
基本類型的方法參數能夠在單獨的線程中安全的修改
String參數是安全的,由於是不可變字符串
StringBuffer參數是不安全的,由於是可變字符串
構造函數通常不須要單選線程安全問題,由於構造函數返回前線程沒有這個對象的引用
利用private和final使字段不可變,而且不編寫可能改變它們的方法
將非線程安全的類用做線程安全類的私有字段,讓包含類以線程安全的方式訪問非安全類
使用atomic包中的線程安全但可變的類,例如AtomicInterger、AtomicLong、AtomicBoolean,AtomicInterger代替int[],不使用引用變量,而是把對象存儲在AtomicStampedReference中
映射和列表使用Collections.synchronizedList()方法進行同步
3.4死鎖
3.4.1兩個線程須要獨佔訪問一樣的資源集,每一個線程分別有其不一樣的子集的鎖,就會發生死鎖
3.4.2確保相同順序請求資源解決死鎖問題
3.5線程調度
3.5.1優先級
設置優先級,範圍0-10,默認爲5,
Thread t =new Thread(); t.setPriority(8);
3.5.2搶佔
搶佔式調度,高優先級線程準備運行時,jvm主動暫停正在運行的低優先級線程,將CPU控制權交給其餘線程
協做式調度,等待正在運行的線程本身暫停,將CPU控制權交給其餘線程
3.5.3阻塞
任什麼時候候線程必須停下來等待它沒有的資源時,就會發生阻塞
讓線程字段放棄CPU控制權,最多見的方式是對I/O阻塞
線程進行同步方法或代碼塊時會產生對鎖的阻塞
3.5.4放棄
Thread.yield()顯示放棄正在運行的線程,擁有得到的鎖,不該該作任何同步
3.5.5休眠
放棄只是線程願意暫停,休眠是直接暫停,擁有得到的鎖,不該該作任何同步
Thread.sleep(100);
喚醒線程
Thread.interrupted();
3.5.6鏈接線程
無限等待被鏈接線程
public final void join()
等待鏈接線程指定時間後,執行本線程
public final synchronized void join(long millis)
3.5.7等待一個對象
處於等待狀態的線程會釋放等待對象的鎖,直到獲得其餘線程的通知(喚醒)
Thread t =new Thread(); t.wait();
通知等待這個對象的線程
Thread t =new Thread(); t.notify();
3.5.8結束
run方法返回時,線程將撤銷,其餘線程能夠接管CPU
3.6線程池和Exeutor
Exeutor能夠很容易創建線程池,只須要將任務做爲一個Runnable對象提交給這個線程,就能夠獲得Future對象,用來檢查任務進度
4.Internet地址
4.1InetAddress類
4.1.1DNS將人民能夠記憶的主機名和計算機能夠記憶的IP地址關聯在一塊兒
4.1.2InetAddress包含一個主機名和一個IP地址
4.1.3建立新的InetAddress對象
根據域名獲取地址
InetAddress address=InetAddress.getByName("www.baidu.com");
獲取主機名
InetAddress address=InetAddress.getByName("www.baidu.com");
address.getHostName();
4.1.4緩存
DNS查找開銷很大,InetAddress會緩存查找的結果
對於不成功的DNS查詢只緩存10秒,API能夠設置查找成功或失敗的緩存時間
4.1.5按IP地址查找
IP地址查找可能不許確,主機名比IP地址穩定,優先使用主機名進行DNS查找
4.1.6安全性問題
任意DNS查找會泄露信息,要禁止任意的DNS查找
4.1.7獲取方法
沒有改變InetAddress對象的字段,InetAddress是不可變,是線程安全的
getHostName()在不字段主機名時纔會聯繫DNS
getCanonicalHostName()知道主機名也會聯繫DNS
InetAddress.getLocalHost()獲取本地主機
4.1.8地址類型
isAnyLocalAddress()是否通配地址,通配地址能夠匹配本地系統中的任何地址
isLoopbackAddress()是否回送地址,回送地址直接在IP層鏈接同一臺計算機,而不使用任何物理硬件
isLinkLocalAddress()是否IPv6連接地址,用於幫助IPv6網絡實現自配置
isSiteLocalAddress()是否IPv6本地網站地址,能夠經過路由器在網站或校園內轉發
isMulticastAddress()是否組播地址,組播會將內容廣播給全部預約的計算機
isMCGlobal()是否全球組播地址,以FF0E或FF1E開頭
isMCOrgLocal()是否組播範圍組播地址,以FF08或FF18開頭
isMCSiteLocal()是否網站範圍組播地址,以FF05或FF15開頭
isMCLinkLocal()是否子網範圍組播地址,以FF02或FF12開頭
isMCNodeLocal()是否本地接口組播地址,以FF01或FF11開頭
4.1.9測試可達性
isReachable(int timeout)測試特定節點是否能夠和本機創建網絡鏈接
4.1.10Object方法
equals(Object obj)是否有相同的IP地址
4.2Inet4Address和Inet6Address
4.2.1經過比較getAddress()返回的字節數組大小能夠區分IPv4和IPv6
4.3NetworkInterface類
4.3.1NetworkInterface類表示一個本地IP地址
4.3.2工廠方法
getName()獲取知道名字的網絡接口
getByInetAddress()與知道IP地址綁定的網絡接口
getNetworkInterfaces()列出本機全部網絡接口
4.3.3獲取方法
getInetAddresses()綁定接口的全部IP地址
4.4一些有用的程序
4.4.1檢查已知地址是否垃圾郵件發送者
4.4.2經過離線處理日誌文件提高WEB服務器的性能
5.URL和URI
5.1URI
5.1.1統一資源標識符是纔有特定語法標識資源的字符串
5.1.2URI組成
模式:模式特定部分
http://www.baidu.com/find?id=101 模式爲http、受權機構爲www.baidu.com、路徑爲/find、查詢爲id=101
5.1.3URLs
URI只能標識資源,URL既能標識資源又能獲取資源
語法:protocol://userInfo@host:port/path?query#fragment
protocol協議
host服務器名字、userInfo用戶信息、port端口,共同構成權威機構
path路徑,執行服務器上的特定目錄
query查詢字符串向服務器提供附加參數
fragment騙的執行遠程資源的某個特定部分
5.1.4相對URL
繼承服文檔的部分信息的不完整的URL稱爲相對URL
<a href="a.html">
相對連接以"/"開頭,標識相對於文檔根目錄而不是當前文件
<a href="/a.html">
5.2URL類
5.2.1使用策略設計模式,協議處理器就是策略,URL類構成上下文,經過它選擇不一樣的策略
5.2.2URL是不可變的,即線程安全的
5.2.3建立新的URL
有多種不一樣的構造函數,根據須要建立
5.2.4從字符串構造URL
URL url=new URL("http://www.baidu.com");
5.2.5由組成部分構造URL
會使用默認端口80
URL url=new URL("http","www.baidu.com","/index.html#intro");
5.2.6構建相對URL
URL url=new URL("http://www.baidu.com/index.html#intro"); URL url2=new URL(url,"index2.html");
5.2.7其餘URL對象來源
5.2.8從URL獲取數據
openStream()能夠得到一個輸入流,能夠由此讀取數據
openConnection()爲指定URL打開一個socket
getContent()向URL請求內容,很難一次得到哪一種對象
getContent(Class[] classes)以指定格式返回URL的內容
5.2.9分解URL
getProtocol()獲取協議
getHost()獲取主機名
getPort()獲取端口號,沒有顯示指定時返回-1
getDefaultPort()獲取默認端口
getFile()獲取路徑部分
getPath()獲取路徑部分,不包括查詢字符串
getRef()獲取片斷標識部分
getQuery()獲取查詢字符串
getUserInfo()獲取用戶信息
getAuthority()獲取受權機構
5.2.10相等性和比較
equals()不會比較標識的資源,會阻塞
5.2.11比較
toURI()將URL對象轉換爲對應的URI對象
5.3URI類
5.3.1URI是對URL的抽象,不計包括統一資源定位符(URL),還包括統一資源名(URN)
5.3.2構造一個URI
URI uri=new URI("http://www.baidu.com/index.html#intro");
5.3.3URI的各部分
語法:模式:模式特定部分:片斷
5.3.4解析相對URI
在相對和以爲URI直接進行轉換
5.3.5相等性和比較
URI能夠排序
5.3.6字符串表示
5.4x-www-form-urlencoded
5.4.1不一樣操做系統會致使URL的不一樣
5.4.2URLEncoder
對URL進行編碼
5.4.3URLDecoder
對URL進行解碼
5.5代理
5.5.1系統屬性
設置代理服務器域名和IP地址
5.5.2Proxy類
爲不一樣的遠程主機選擇不一樣的代理服務器
5.5.3ProxySelector類
根據協議、主機、路徑、日期時間和其餘表中來選擇不一樣的代理
5.6經過GET與服務端程序通訊
5.7訪問口令保護的網站
5.7.1Authenticator類
爲使用HTTP認證自我保護的網站提供用戶名和口令
5.7.2PasswordAuthentication類
包含用戶名和口令的兩個只讀屬性的類
5.7.3JPasswordField類
以稍安全的方式詢問用戶口令
6.HTTP
6.1HTTP協議
6.1.1響應碼
100-199提供信息的響應
200-299成功
300-399重定向
400-499客戶端錯誤
500-599服務器錯誤
6.1.2kepp-Alive
重用socket鏈接
6.2HTTP方法
6.2.1GEY
6.2.2POST
6.2.3PUT
6.2.4DELETE
6.3請求主體
6.3.1起始行
方法、路徑和查詢字符串,以及HTTP版本
6.3.2HTTP首部
6.3.3空行
6.3.4主體
6.4Cookie
6.4.1小文本串在鏈接直接儲存持久的客戶端狀態,小文本串稱爲cookie
6.4.2CookieManager儲存獲取cookie的類
6.4.3CookieSore本地存放和獲取Cookie
7.URLConnection
7.1打開URLConnection
7.1.1構造URL對象
7.1.2獲取URLConnection對象
7.1.3配置URLConnection
7.1.4讀取首部字段
7.1.5得到輸入流讀取數據
7.1.6得到輸出流寫入數據
7.1.7關閉鏈接
7.2讀取服務器的數據
7.2.1構造URL對象
7.2.2獲取URLConnection對象
7.2.3獲取輸入流
7.2.4讀取數據
7.3讀取首部
7.3.1獲取指定的首部字段
7.3.2獲取任意首部字段
7.4緩存
7.4.1Java的Web緩存
7.5配置鏈接
7.5.1protected URL url獲取url
7.5.2protected boolean connected鏈接是否打開
7.5.3protected boolean allowUserInteraction是否容許用戶交互
7.5.4protected boolean doInput是否容許讀取服務器
7.5.5protected boolean doOutput是否運行將輸出發回服務器
7.5.6protected boolean ifModifiedSince是否在指定時間後有所修改
7.5.7protected boolean useCaches是否可使用緩存
7.5.8超時
查詢和修改鏈接的超時值
7.6配置客戶端請求HTTP首部
7.7向服務器寫入數據
7.8URLConnection的安全考慮
7.9猜想MIME媒體類型
7.10HttpURLConnection
7.10.1請求方法
7.10.2HEAD請求HTTP首部
7.10.3DELETE刪除WEB服務器上指定URL的文件
7.10.4.PUT將一個文件存放在web服務器上
7.10.5OPTIONS詢問某個特定URL支持哪些選項
7.10.6TRACE請求HTTP首部
7.10.7端口與服務器的鏈接
7.10.8處理服務器響應
錯誤條件
重定向
代理
流模式
8.客戶端Socket
8.1使用Socket
8.1.1Socket是兩臺註解直接的一個鏈接,能夠完成7個基本操做:鏈接遠程機器、發送數據、接受數據、關閉鏈接、綁定端口、監聽入站數據、在綁定端口上接受來自遠程機器的鏈接
8.2用Telnet研究協議
8.2.2用Telnet研究協議,瞭解協議如何操做
8.2.3用Socket從服務器讀取
try (Socket socket = new Socket("www.baidu.com",8080)){
//設置鏈接超時時間
socket.setSoTimeout(1000); InputStream inputStream = socket.getInputStream(); } catch (Exception e) { e.printStackTrace(); }
8.2.4用Socket寫入服務器
OutputStream outputStream = socket.getOutputStream();
8.2.5半關閉Socket
shutdownInput()只關閉輸入流
isInputShutdown()輸入流是否打開
8.3構造和鏈接Socket
8.3.1基本構造函數
購貨函數返回前能夠得到與遠程主機創建的活動的網絡鏈接,能夠用這個對象肯定是否容許某個端口創建鏈接
8.3.2選擇從那個本地接口鏈接
Socket(String host, int port, InetAddress localAddr, int localPort)
8.3.3構造但不鏈接
經過無參構造得到Socket,再經過地址創建鏈接,並設置超時等待時間,來支持不一樣類型的Socket
try (Socket socket = new Socket()){ SocketAddress address=new InetSocketAddress("www.baidu.com",8080); socket.connect(address,10000); } catch (Exception e) { e.printStackTrace(); }
8.3.4Socket地址
getRemoteSocketAddress()獲取鏈接系統的地址
getLocalSocketAddress()返回發起鏈接的地址
8.3.5代理服務器
Socket(Proxy proxy)建立一個未鏈接的Socket,指定一個代理服務器鏈接
8.3.6獲取Socket的信息
getInetAddress()獲取遠程主機
getPort()獲取遠程端口
getLocalAddress()本地鏈接地址
getLocalPort()本地鏈接端口
8.3.7關閉仍是鏈接
isClosed()檢查Socket是否關閉,從未鏈接也是false
isConnected()檢查Socket是否從未鏈接過遠程主機
//檢查Socket是否打開 if (socket.isConnected() && !socket.isConnected()) { }
8.3.8toString()
用於調試
8.4設置Socket選項
8.4.1TCP_NODELAY是否關閉Socket的緩衝,遊戲程序須要設置爲ture
8.4.2SO_BINDADDR指定Socket關閉時是否將還沒有發送的數據報進行發送(延遲時間不爲0),默認系統在延遲時間爲設置不爲0時會在Socket關閉後嘗試發送剩餘數據
8.4.3SO_TIMEOUT設置讀取數據的超時時間,默認爲0,不限制
8.4.4SO_RCVBUF控制用於網絡輸入的建議的接收緩衝區大小,SO_SNDBUF控制用於網絡輸入的建議的發送緩衝區大小,二者相斥,會使用較小的設置值。若是帶寬大,數據傳輸率小,應該增長緩衝區大小,相反則減小
8.4.5SO_KEEPALIVE打開後,客戶端會2小時發送一個數據報,確保服務器未崩潰
8.4.6SO_OOBINLINE是否接收緊急數據
8.4.7SO_REUSEADDR是否運行其餘Socket綁定到已知端口
8.4.8IP_TOS服務類型
服務類型儲存在IP首部
8.5Socket異常
8.5.1BindException端口正在使用或沒有足夠權限
8.5.2ConnectException主機忙或沒有進程監聽端口
8.5.3NoRouteToHostException鏈接超時
8.5.4ProtocolException數據違反TCP/IP規範
8.6GUI應用中的Socket
8.6.1whois簡單目錄服務協議
8.6.2網絡客戶庫
9.服務器Socket
9.1使用ServerSocket
9.1.1建立一個迭代服務器
accept()是阻塞的
try (ServerSocket serverSocket = new ServerSocket(8080)) {
while (true) {
try (Socket socket = serverSocket.accept()) {
Writer out = new OutputStreamWriter(socket.getOutputStream());
//向客戶端回送消息
out.flush();
socket.close();
} catch (IOException e) {
}
}
} catch (IOException e) {
System.out.println(e);
}
9.1.2提供二進制數據
使用寫byte數組的輸出流
9.1.3多線程服務器
多線程服務器
客戶端沒有使用try-with-resources是由於客戶端Socket不可了try塊,而放在單獨的線程中。若是使用了,主線程一旦到達while循環末尾就會關閉socket,,而次數新線程尚未用完這個Socket
public class test { public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(8080)) { while (true) { try (Socket socket = serverSocket.accept()) { Thread task = new MyThread(socket); task.start(); } catch (IOException e) { } } } catch (IOException e) { System.out.println(e); } } } class MyThread extends Thread { private Socket socket; public MyThread(Socket socket) { this.socket = socket; } @Override public void run() { try { Writer out = new OutputStreamWriter(socket.getOutputStream()); //向客戶端回送消息 out.flush(); socket.close(); } catch (IOException e) { System.out.println(e); } finally { try { socket.close(); } catch (IOException e) { //忽略 } } } }
線程池服務器
public class test { public static void main(String[] args) { ExecutorService pool = Executors.newFixedThreadPool(50);//生成包含50個線程的線程池 try (ServerSocket serverSocket = new ServerSocket(8080)) { while (true) { try { Socket socket = serverSocket.accept(); Callable<Void> task = new MyThread(socket); pool.submit(task); } catch (IOException e) { } } } catch (IOException e) { System.out.println(e); } } } class MyThread implements Callable<Void> { private Socket socket; public MyThread(Socket socket) { this.socket = socket; } @Override public Void call() { try { Writer out = new OutputStreamWriter(socket.getOutputStream()); //向客戶端回送消息 out.flush(); socket.close(); } catch (IOException e) { System.out.println(e); } finally { try { socket.close(); } catch (IOException e) { //忽略 } } return null; } }
9.1.4用Socket寫入服務器
獲取輸入流進行寫入
9.1.5關閉服務器Socket
關閉Socket而非ServerSocket
9.2日誌
9.2.1日誌記錄內容
請求日誌
服務器錯誤日誌
9.2.2如何記錄日誌
爲每個類建立日誌根據,日誌工具是線程安全的
捕獲RuntimeException可以避免服務器崩潰,使服務器繼續處理其餘請求
public class test {
private final static Logger errorLogger = Logger.getLogger("errors");
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(50);//生成包含50個線程的線程池
try (ServerSocket serverSocket = new ServerSocket(8080)) {
while (true) {
try {
Socket socket = serverSocket.accept();
Callable<Void> task = new MyThread(socket);
pool.submit(task);
} catch (IOException e) {
errorLogger.log(Level.SEVERE, "accept errer", e);
} catch (RuntimeException e) {
errorLogger.log(Level.SEVERE, "unexpected errer" + e.getMessage(), e);
}
}
} catch (IOException e) {
errorLogger.log(Level.SEVERE, "Couldn't start server", e);
} catch (RuntimeException e) {
errorLogger.log(Level.SEVERE, "Couldn't start server:" + e.getMessage(), e);
}
}
}
9.3構造服務器Socket
9.3.1建立一個保存50個入站鏈接的服務器Socket
ServerSocket serverSocket = new ServerSocket(8080,50)
9.3.2構造但不綁定端口
容許程序在綁定端口以前設置服務器選項
9.4得到服務器Socket的有關信息
9.4.1getInetAddress()獲取服務器使用的地址
9.4.2getLocalPort()獲取服務器使用的端口
9.5Socket選項
9.5.1SO_TIMEOUT等待鏈接超時時間
9.5.2SO_REUSEADDR是否容許新的Socket綁定到以前使用過的端口上
9.5.3SO_RCVBUF緩衝區大小
9.5.4服務類型
低成本、高可靠、最大吞吐量、最小延遲
9.6HTTP服務器
9.6.1單文件服務器
9.6.2重定向服務器
9.6.3功能完備的HTTP服務器
10.安全Socket
10.1保護通訊
對稱密鑰和非對稱密鑰與中間人攻擊
10.2建立安全客戶端Socket
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); try (Socket socket = factory.createSocket("www.baidu.com", 8080)) {
10.3選擇密碼組
密碼組保護:協議、密鑰交換算法、加密算法和校驗和
10.4事件處理器
實現HandshakeCompletedListener接口跨越知道SSLSocket的握手結束
10.5會話管理
相同回話使用一組相同的公開密鑰和私有密鑰
10.6客戶端模式
設置Socket是否須要自行認證,僅用於高安全性的內部應用
10.7建立安全服務器Socket
10.7.1步驟
10.8配置SSLServerSocket
10.8.1選擇密碼組
10.8.2會話管理
10.8.3客戶端模式
11.非阻塞I/O
11.1一個實例客戶端
11.1.1通道
利用通道不須要獲取輸入或輸出流,能夠直接寫入通道自己,不是寫入字節數組,而是寫入ByteBuffer對象
11.1.2緩衝區
緩衝區能夠重用,再次使用前須要清空緩衝區
try { SocketAddress address = new InetSocketAddress(8080); SocketChannel client = SocketChannel.open(address);//打開通道 ByteBuffer buffer = ByteBuffer.allocate(74);//建立ByteBuffer對象 WritableByteChannel out = Channels.newChannel(System.out);//將數據封裝在通道中 client.configureBlocking(false);//設置爲非阻塞模式 while (true) { int n = client.read(buffer); if (n > 0) { buffer.flip();//迴繞,使輸出通道從數據的開頭寫入 out.write(buffer);//將數據寫入 buffer.clear();//清空緩衝區便於複用 } else if (n == -1) { break;//服務器故障 } } } catch (IOException e) { errorLogger.log(Level.SEVERE, "accept errer", e); }
11.2一個示例服務器
public class test { private final static Logger errorLogger = Logger.getLogger("errors"); public static void main(String[] args) { ServerSocketChannel serverChannel; Selector selector; try { serverChannel = ServerSocketChannel.open();//打開通道 serverChannel.bind(new InetSocketAddress(8080));//綁定端口 serverChannel.configureBlocking(false);//設置爲非阻塞模式 selector = Selector.open();//迭代處理準備好的鏈接代替爲鏈接分配線程 serverChannel.register(selector, SelectionKey.OP_ACCEPT);//註冊監聽通道的選擇器,關注的操做爲通道是否準備好接受一個新鏈接 } catch (IOException e) { errorLogger.log(Level.SEVERE, "accept errer", e); return; } while (true) { try { selector.select();//檢查是否有可操做的數據 } catch (IOException e) { errorLogger.log(Level.SEVERE, "accept errer", e); break; } Set<SelectionKey> readyKeys = selector.selectedKeys();//找到了就緒的通道 Iterator<SelectionKey> iterator = readyKeys.iterator(); if (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove();//刪除已經處理的鍵,避免處理2次 try { if (key.isAcceptable()) {//服務器通道 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE); //爲客戶端創建緩衝區 ByteBuffer buffer = ByteBuffer.allocate(74); //讀取數據 buffer.flip();//迴繞,使輸出通道從數據的開頭寫入 key2.attach(buffer);//將數據寫入 } else if (key.isWritable()) {//客戶端通道 SocketChannel client = (SocketChannel) key.channel(); //向客戶端寫入數據 ByteBuffer buffer = (ByteBuffer) key.attachment(); if (!buffer.hasRemaining()) { buffer.rewind();//用下一行從新填充緩衝區 //將數據放入緩衝區 buffer.flip();//準備從緩衝區寫入 } client.write(buffer); } } catch (IOException e) { key.cancel(); try { key.channel().close(); } catch (IOException ex) { errorLogger.log(Level.SEVERE, "accept errer", ex); } } } } } }
11.3緩衝區
11.3.1流是基於字節的,通道是基於塊的,通道支持對統一對象的讀/寫,緩衝區能夠看着固定大小的元素列表
11.3.2緩衝區的4個關鍵部分
位置,緩衝區將讀取或寫入的下一個位置
容量,緩衝區能夠保存的元素的最大書面
限度,緩衝區可訪問數據的末尾位置
標記,緩衝區客戶端指定的因此
rewind()將位置設爲0,但不改變限度,容許從新讀取緩衝區
flip()將限度設置爲讀取位置,位置設置爲0,用於排課剛剛填充的緩衝區
11.3.3建立緩衝區
分配,allocate()用於輸入,建立後備數組
ByteBuffer buffer=ByteBuffer.allocate(100);
直接分配,allocateDirect(),不建立後備數組
包裝,wrap()用於輸出,對數組操做結束前不要包裝數組
byte[] bytes = "som data".getBytes("UTF-8"); ByteBuffer buffer=ByteBuffer.wrap(bytes);
填充,向緩衝區放入數據
排空,從緩衝區獲取數據,須要進行迴繞,使位置回到0
11.3.4批量方法
批量填充和排空相應元素數組
11.3.5數據轉換
11.3.6視圖緩衝區,肯定知道讀取的數據是一種特定的基本數據類型,能夠建立視圖緩衝區
11.3.7壓縮緩衝區,壓縮時將緩衝區剩餘數據移到開頭,釋放空間
11.3.8複製緩衝區,創建緩衝區的副本將相同信息分發到多個通道
11.3.9分片緩衝區,分片是原緩衝區的子序列,只保護當前位置到限度的全部元素
11.3.10標記和重置,用於從新讀取數據
11.3.11Object方法
相等:類型相同,剩餘個數相同,相對位置元素相等,不考慮位置以前的元素和日曆、限度、標記
11.4通道
11.4.1SocketChannel
鏈接,有參構造創建鏈接,無參構造不當即鏈接
讀取,一個源填充多個緩衝區,稱爲散佈
寫入,將多個緩衝區數據寫入一個Socket,稱爲聚焦
關閉
11.4.2ServerSocketChannel
用於接收入站鏈接
建立服務器Socket通道,open()
接受鏈接,accept()
11.4.3Channels類
工具類,將傳統的基於I/O的流、閱讀器和書寫器包裝在通道中
11.4.4異步通道
11.4.5Socket選項
11.5就緒選擇
11.5.1Selector類
選擇讀寫時不阻塞的Socket
open()建立選擇器
register()註冊通道
準備好的通道在使用後,須要刪除這個鍵
11.5.2SelectorKey類
至關於通道的指針
channel()獲取通道
attachement()獲取儲存的對象
cancel撤銷註冊
12.UDP
12.1UDP協議
12.2UDP客戶端
public static void main(String[] args) { try (DatagramSocket socket = new DatagramSocket((0))) { socket.setSoTimeout(10000);//設置超時時間 InetAddress host = InetAddress.getByName("ww.baidu.com"); DatagramPacket request = new DatagramPacket(new byte[1], 1, host, 8080);//發送數據包 DatagramPacket response = new DatagramPacket(new byte[1024], 1024);//接受數據包 socket.send(request);//發送數據 socket.receive(response);//接收響應 String result = new String(response.getData(), 0, response.getLength(), "UTF-8"); } catch (IOException e) { e.printStackTrace(); } }
12.3UDP服務器
public static void main(String[] args) { try (DatagramSocket socket = new DatagramSocket((8080))) { while (true) { try { DatagramPacket request = new DatagramPacket(new byte[1024], 1024);//發送數據包 socket.receive(request);//接收響應 String daytime = new Date().toString(); byte[] data = daytime.getBytes("UTF-8"); DatagramPacket response = new DatagramPacket(data, data.length, request.getAddress(), request.getPort());//接受數據包 socket.send(response);//發送數據 } catch (IOException | RuntimeException ex) { ex.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } }
12.4DatagramPacket類
12.4.1數據包結構
12.4.2構造函數
接收數據包的構造函數
public DatagramPacket(byte buf[], int offset, int length)
發送數據包的構造函數
public DatagramPacket(byte buf[], int length,InetAddress address, int port)
12.4.3get方法
獲取註解地址、端口、數據報數據、數據字節數、開始填充數據報的位置
12.4.4set方法
設置有效載荷,其餘途徑發送大數據、發往的地址、發往的端口、發送數據的成都
12.5DatagramSocket類
12.5.1構造函數
public DatagramSocket()建立一個綁定到匿名端口的Socket
public DatagramSocket(int port)建立一個指定端口的Socket
public DatagramSocket(int port, InetAddress laddr)用於多宿主主機
12.5.2發送和接收數據報
send()發送數據
receive()接收數據
12.5.3管理鏈接
12.5.4Socket選項
12.6一些有用的應用程序
12.6.1簡單的UDP客戶端
12.6.2UDPServer
12.6.3UDP Echo客戶端
12.7DatagramChannel
12.7.1用於非阻塞UDP應用程序
12.7.2使用DatagramChannel
打開Socket、接收、發送、鏈接、讀取、寫入、關閉
12.7.3Socket選項
13.IP組播
13.1組播
13.1.1組播比單播的點通訊寬,但比廣播通訊窄並且目標更明確
13.1.2組播地址與組
組播地址又叫組播組,是一組共享一個組播地址的主機
13.1.3客戶端和服務器
使用UDP,須要額外設置TTL
13.1.4路由器和路由
最大限制在因而否有特殊的組播路由器
13.2使用組播Socket
13.2.1構造函數
13.2.2與組播組通訊
加入組
離開組而且關閉鏈接
發送組播數據
回送模式
網絡接口
13.3兩個簡單示例