這一篇是學習網絡編程的日誌,在開始學習之前,我盡量以一個編程白癡的角度,把我的心路歷程記錄下來,我覺的從一個編程小白到入門級新手是一個很短的距離。
既然學習網絡,首先我們要考慮到的就是:
1、網絡是什麽或者網絡編程的基礎?
作為一個21世紀的人,對於這個問題,肯定能聯想或者回答點什麽,比如網絡就是要有網線、設備(專業術士:終端),比如臺式電腦(PC)、手機、IPad、電視等,似乎只能想到這些了,但是作為一個編程的人員,似乎要了解的更加深入和具體一點,因為以上內容並不能支持你去完成編程。
這需要不斷的提出問題,從各個可能角度去了解,比如:
- 網絡的實際工作是什麽?答:完成信息的傳遞.
- 舉例信息傳遞的一個正式場景?答:通過QQ給好友發送信息,或者查看門戶網站的新聞。
- 假設讓你來設計QQ,怎麽完成給某個好友發信息?答:首先我要知道這個好友在網絡上位置,我們知道通過IP,但是這個信息發到好友的PC上,怎麽到的QQ,而不是feiQ、MSN或者其它任何可以接受信息的軟件上的呢?
- 假設我的好友不在線,我發送信息過去怎麽辦?答:發送失敗,返回一個提示給你。
以上問題的思考,引入我們網絡編程專業知識的介紹:
- 網絡模型
1、 什麽是網絡模型?
個人理解:模型是把還未存在或者抽象的東西用更具體的方式表現出來;所以網絡模型是把信息傳輸過程中相關的軟硬件,按功能進行劃分,便於我們清晰的了解網絡。
OSI模型,所謂的七層模型,早期的。
關於OSI模型各層的功能,在百科上有詳細的介紹,但是比較難以理解和記憶,所以根據畢向東老師的視頻,做一下簡單的記錄:
1、 物理層:可以理解為網絡中的物理媒介,比如網線,用於比特流的傳輸。
2、 數據鏈路層:主要將物理層接收的MAC地址進行封裝和解封裝,主要設備就是交換機。
3、 網絡層:將下層接收到的數據進行IP地址的封裝與解封裝,主要設備就是路由器。
4、 傳輸層:定義了一些傳輸的協議和端口號,我們主要學習的內容。
5、 會話層:通過傳輸層(傳輸端口和接收端口)建立數據傳輸的通路。
6、 表示層:對於接收的數據進行解釋、加密和解密、壓縮和解壓縮,就是把二進制變成文字、圖片、聲音等。
7、 應用層:指計算機上的應用軟件,比如FTP、瀏覽器、QQ,它們其實就是數據解析器。
封包:首先,軟件發送信息後,應用層需要帶上該軟件的標識,然後表示層把解析數據,明確發送的是圖標、聲音等,接著會話層需要明確與誰建立通話,傳輸層明確使用什麽協議傳輸,網絡層需要確定發送到IP地址,數據鏈路層中交換機根據IP綁定對應的MAC地址,物理層把信息變成二進制流進行傳輸。
拆包:對方物理層接收二進制流,數據鏈路層查看MAC地址是否與本機一致,網絡層查看IP地址是否與本機一致,傳輸層明確查看是什麽協議,會話層查看與那個應用建立的回話,表示層查看發送的什麽內容,應用層查看那個應用接收數據並解析。
TCP/IP模型,四層結構,可理解為了簡化七層模型而提出的,兩者模型的對照關系如下:
(應用層、表示層、會話層)變成“應用層”,(物理層、數據鏈路層)變成“主機至網絡層”,又稱網絡接口層,其中網絡層和傳輸層沒變,這兩個層比較重要。
關於這張圖,是對TCP/IP模型直觀的描述,首先網絡接口層,所謂的網絡接口1、2、3,可以看做是交換機的中轉特性,網際層主要是IP的封裝,傳輸層主要是傳輸協議的綁定和解析,而應用層中不同協議的,如HTTP,可以看做是應用軟件通訊間的協議。
2、為什麽有了TCP/IP、UDP協議,還需要HTTP、STMP協議?
可以這樣理解,TCP/IP協議只是滿足了最原始數據的交互,但是有些應用軟件,比如瀏覽器,它需要解析HTML格式的數據,那麽解決辦法就是再建一個自身軟件相關的協議,當然這種協議都是針對廣泛的應用而誕生的,如郵件、網頁等,TCP/IP協議屬於比較底層的協議,HTTP是在此基礎上建立的協議。
- 網絡通訊的要素
根據上面的學習,我們可以把網絡通訊中最關鍵的要素提取出來:
1、 信息發到哪裏,家住在哪裏?即計算機的地址,MAC或者IP,IP(Internet Protocol Address),不是IP協議。
2、 怎麽傳輸?用馬、飛鴿、自行車、摩托車還是其它工具?通訊方式(協議):TCP/IP、UDP。
3、 家裏哪一個人收?因為一家裏面住了很多人,信送到家裏需要指明收信人,同理,一臺計算機上有很多軟件,信息發過去,究竟是那個一個軟件接收,QQ還是feiQ?這就是端口號,比如瀏覽器默認是80端口。一個端口對應一個操作系統進程。
總結:三大要素分別為,IP地址、端口號、傳輸協議。
1、 IP地址
是計算機在網絡中唯一的身份標識,就好比人的身份證一樣。IP一般由網絡運營商指定,區別於MAC地址,是可以改變的(可以重新指定)。
IP由四段組成,每段有8位二進制組成,最大值為255,現有IP組合個數為2的32次方,也只有42億,已經不滿足現有需要了,所以提出了IPv6。
本機的地址為:127.0.0.1,機器名為:localhost,每個網絡段都有一個廣播地址就是“xxx.xxx.xxx.255”,如果向這個IP發送信息,則該網段上所有機器都會接收信息。
2、 端口號
是應用軟件在計算機上的邏輯標識,更準確的說是進程在計算機中的標識,它不是真實的硬件端口,而是操作系統層面賦予軟件的邏輯地址,目的就是為把信息準確的發送給不同的進程。
有效端口號的範圍:0~65535,其中0~1024是系統使用或者保留端口。
3、 傳輸協議
所謂協議就是規則,傳輸協議有兩種:TCP/IP和UDP
TCP/IP(傳輸控制協議),特點:
1) 通過三次握手建立穩定的數據通道,所謂三次握手,就是發送端首先發送連接請求,接收端接收請求信息,這是第一次握手;然後接收端返回的信息表示:“接收到你的信息了”,這個信息又返回到發送端,這是第二次握手;最後發送還得發送信息:“接收到你返回的信息了”給接收端,這是第三次握手。
為什麽要三次握手,而不是兩次或者是四次?因為有可能在第二次握手後,發生斷網等異常,那麽發送信息就會失敗;為什麽不是四次,因為可以把3次看作是最少且合適的次數,四次可能是重復行為了。
2) 一次可以發送大量數據,沒有大小的限制。
UDP協議(數據報文協議),特點:
1) 發送端直接發送數據,事先不建立穩定色數據通道,數據可能會丟失,是不安全的。
2) 沒有發送端的數據大小最大只能為64K。
- 域名解析
1、 什麽是域名?它的由來?
簡單的來說就是網址,比如www.googel.com,域名其實是IP的一個替代,因為正常訪問互聯網是需要那臺主機的IP地址的,比如10.0.0.100,但是大量的IP是不利於記憶的,需要用域名這種方式更加方便。
2、 域名解析的方式?
雖然我們在上層中使用的是域名,但是在網絡層,我們封裝的還是IP地址,所以首先需要把我們訪問的域名解釋為對應的IP,這個工作由DNS(域名解析服務)服務器來做,DNS服務器分為本地和互聯網上的,優先調用本地DNS服務
這個服務是調用的文件就是:C:\Windows\system32\drivers\etc\hosts:
本質上就是IP與域名的對應關系,在我們上網時,根據域名去對應到IP地址,然後根據IP地址去查找主機,同樣在我們配置IP地址時,有一個DNS地址,那個就是外網的DNS服務器地址,一般有網絡運營商提供。
2、在程序設計中,根據以上的內容,為我們提供哪些對應的類或者接口。
- IP地址。
在Java中萬物即對象,所以看起來IP地址是一串字符串,並沒有成為對象的必要,但是在java確是對象,類名為InetAddress,包名:java.net。
1、 怎麽獲取一個InetAddress對象?
我們最熟悉的當時是字符串形式的,比如“192.168.0.1”,所以容易想到的就是對象初始化的時候,在構造函數中傳入該字符串,但是你查詢API發現該類並沒有構造函數,它其實是提供的靜態方式,接收字符串,返回InetAddress對象。
import java.net.*; class IPDemo { public static voidmain(String[] args) throws UnknownHostException{ //getByName()方法,接收字符串(可以為IP、主機名、域名),返回該IP對象 InetAddress ip =InetAddress.getByName("www.Google.com"); //getLoaclHost(),返回本機IP InetAddresslocal_ip = InetAddress.getLocalHost(); //getHostAddress(),返回IP地址的字符串形式,根據DNS解析規則 String str_ip = ip.getHostAddress(); System.out.println(str_ip); //getHostName(),返回IP地址的主機名,沒有,則返回IP地址 String str_name =ip.getHostName(); System.out.println(str_ip); } }
- Socket
1、 為什麽現在不直接學習JAVA的中網絡編程的相關類,而是現在了解socket?
socket,專業術士叫套接字,當然沒必要糾結這個名字,重要的是它的由來,他是網絡編程的提出一種規範,也就是說它不是哪一種編程語言裏面提出的,而是所有編程語言在涉及到網絡編程,必須通過Socket機制實現,它淩駕於編程語言之上。
2、 socket機制有哪些特點?
首先socket,它是傳輸層、網絡層、網絡接口層向上為應用層提供的編程接口;
它接收網絡通訊的關鍵要素(IP、端口)和傳輸的數據,它底層自動完成相關通訊協議、IP封裝和解析、IO流轉換和傳輸;
通訊兩端都是必須是socket對象,而兩者之間的流,又可以稱為socket流,所以網絡編程又稱為socket編程。它就好比是兩個貨運碼頭,在發送端,你把IP、端口、數據信息放到socket中,在另外的接收端,同樣建立一個socket對象,並且只要指定它監聽本機的端口,就能接收到發送端信息。
3、 通訊要素中,關於通訊協議,比如UDP、TCP/IP,在socket是怎麽去體現或者是區別?
Java中不同協議的區別,並不是在一個socket類初始化的時候去指定的,而是為這兩者協議提供了不同的java類。
UDP:發送和接收端即為DatagramSocket。
TCP/IP:分為客戶端和服務端類,客戶端就是:Socket,而服務端是:ServerSocket。
- UDP協議
首先查看JDK API文檔,java.net.DatagramSocket,此類表示用來發送和接收數據報包的套接字。
構造函數:
DatagramSocket
()構造數據報套接字並將其綁定到本地主機上任何可用的端口。
DatagramSocket
(int port)創建數據報套接字並將其綁定到本地主機上的指定端口。
發送方法:
voidsend
(DatagramPacket p)
從此套接字發送數據報包。接收方法:
voidreceive
(DatagramPacket p)
從此套接字接收數據報包。
這裏要說明的不是我們想象中,直接接收IP、端口和數據,而是一個叫“DatagramPacket”的類,關於這個類我們查看API,說明如下:
DatagramPacket:此類表示數據報包,數據報包用來實現無連接包投遞服務。每條報文僅根據該包中包含的信息從一臺機器路由到另一臺機器。從一臺機器發送到另一臺機器的多個包可能選擇不同的路由,也可能按不同的順序到達。不對包投遞做出保證。
再查看它其中的構造函數:
DatagramPacket
(byte[] buf,int length,InetAddressaddress,int port)構造數據報包,用來將長度為
length的包發送到指定主機上的指定端口號。
備註:它其實就是把上面提到的信息封裝到一個叫數據報包的對象中,這樣我們在數據傳輸中其實是傳遞的數據報包,這種設計方式更加符合socket底層數據傳輸的真實情形。
DatagramPacket
(byte[] buf, int length)構造
DatagramPacket,用來接收長度為length的數據包。
需要註意的是:
DatagramPacket是分發送報包和接收報包,它的區別就是接收報包創建時,構造函數不需要傳入端口和IP。
UDP分為發送端和接收端
1、 UDP發送端。
建立發送端的思路:
1) 創建socket對象,即DatagramSocket。
2) 創建需要傳輸的數據報包,即DatagramPocket對象,分別傳入需要傳輸的數據(字節數組)、字節數組的長度、IP對象、端口。
3) 調用socket的發送方法,即send(),傳入已創建的數據報包。
4) 關於socket流,即調用close()方法。
import java.net.*; import java.io.*; class UdpSend { public static voidmain(String[] args) throws IOException,UnknownHostException { //創建socket對象 DatagramSocketds = new DatagramSocket(8888); //發送的信息 String info= "網絡編程,我來了!"; byte[] buf =info.getBytes(); //創建發送的數據包對象 DatagramPacketdp = new DatagramPacket(buf,buf.length,InetAddress.getLocalHost(),10000); //通過socket對象發送數據包 ds.send(dp); //關閉socket流對象 ds.close(); } }
2、 UDP接收端
建立接收端的思路:
1) 創建接收端sockect服務。
2) 創建接收端的數據包。
3) 通過socket服務的接收方法把數據存儲到數據包中。
4) 通過數據包的方法解析其中的數據
5) 關閉資源。
importjava.net.*; importjava.io.*; class UdpReceive{ public static void main(String[] args)throws IOException,UnknownHostException { //創建socket對象,指定監聽端口10000 DatagramSocket ds = newDatagramSocket(10000); //定義一個字節數值用來接收信息 byte[] buf = new byte[1024]; System.out.println(dp.getLength()); //創建接收的數據包對象 DatagramPacket dp = newDatagramPacket(buf,buf.length); System.out.println(dp.getLength()); //通過socket對象接收數據包 ds.receive(dp); System.out.println(dp.getLength()); //通過包的方法解析相關信息,比如IP、端口、信息 String ip =dp.getAddress().getHostAddress(); int port = dp.getPort(); String info = newString(dp.getData(),0,dp.getLength()); //打印接收到的信息 System.out.println(ip+","+port+","+info); //關閉socket流對象 ds.close(); } }
註意點:
1、 DatagramPacket對象構建是需要指定長度的,一般都為發送或者接收的字節數組的長度。
2、 在服務端中創建DatagramSocket對象,需要指定監聽的端口號;接收到的數據包中getPort()方法返回的是發送端主機的端口,即發送端示例中的8888,而不是10000。
3、 DatagramPacket的內部消息長度值在接收數據後會發生改變,變為實際接收到的數據的長度值
4、 如果發送端的數據為大量,則可以在服務端通過循環的方式接收,暫時沒有學習相關代碼。
importjava.net.*; importjava.io.*; class ChatClientimplements Runnable { public void run() { try { //創建socket對象 DatagramSocket ds=new DatagramSocket(8888); //鍵盤輸入流 BufferedReader br =new BufferedReader(new InputStreamReader(System.in)); //讀取字符 String line = null; while(true){ line =br.readLine(); byte[] buf= line.getBytes(); //創建發送的數據包對象 DatagramPacketdp = new DatagramPacket(buf,buf.length,InetAddress.getLocalHost(),10000); //通過socket對象發送數據包 ds.send(dp); if(line.equals("886")){ break; } } //關閉socket流對象 ds.close(); }catch(Exception e){ } } }
importjava.net.*; importjava.io.*; class ChatServerimplements Runnable{ public void run() { try{ //創建socket對象,指定監聽端口10000 DatagramSocket ds =new DatagramSocket(10000); //定義一個字節數值用來接收信息 byte[] buf = newbyte[1024]; //創建接收的數據包對象 DatagramPacket dp =new DatagramPacket(buf,buf.length); String info = null; //通過socket對象接收數據包 while(true){ ds.receive(dp); info = newString(dp.getData(),0,dp.getLength()); //打印接收到的信息 System.out.println(dp.getAddress().getHostAddress()+":"+info); } }catch(Exception e){ } } }
class ChatDemo { public static void main(String[] main){ new Thread(newChatClient()).start(); new Thread(newChatServer()).start(); } }
- TCP
回顧一下tcp的主要特點,通過三次握手建立的穩定通道,進行數據的傳輸。
tcp的類在java中定義與UDP是不一樣的,它有兩個類:客戶端socket和服務端socket。
客戶端類:
Socket,此類實現客戶端套接字(也可以就叫“套接字”)。套接字是兩臺機器間通信的端點。
服務端類:
ServerSocket,此類實現服務器套接字。服務器套接字等待請求通過網絡傳入。它基於該請求執行某些操作,然後可能向請求者返回結果。
1、 客戶端
首先查看客戶端類Socket的相關方法。
構造函數
Socket
(InetAddress address,int port)創建一個流套接字並將其連接到指定 IP 地址的指定端口號。
Socket
(Stringhost, int port)創建一個流套接字並將其連接到指定主機上的指定端口號。
1) 接著我們要在API中查看相關的發送和接收方法,就跟UDP一樣,但是你發現並沒有,這是為什麽?
因為tcp是要建立傳輸通道的,那麽它是在什麽時候建立的呢,其實就是在我們創建Socket對象時,它就會嘗試建立通道,如果服務端沒有響應,那麽就會報錯;否則,建立數據通道,而數據通道本質上就是IO流,而且是雙向通道。所以我們會發現Socket類中有返回IO流的方法。
返回輸出流
OutputStreamgetOutputStream
()
返回此套接字的輸出流。返回輸入流
InputStream
getInputStream
()
返回此套接字的輸入流。關閉此套接字
voidclose
()
關閉此套接字。
所以我們必須借助IO流,實現數據的讀寫操作。
客戶端的建立思路
1) 建立socket服務。
2) 返回socket的輸入流。
3) 通過IO流把傳輸信息寫入。
4) 關閉Socket資源。
importjava.net.*; importjava.io.*; classTCPClientDemo { public static void main(String[]args) throwsSocketException,IOException{ //1.建立socekt服務 Socket client = newSocket(InetAddress.getLocalHost(),10001); //2.返回輸出數據流 OutputStream out =client.getOutputStream(); //3.創建傳輸數據 String str = "tcp客戶端:我來了!"; //4.通過IO流的wirte()方法寫入傳輸數據 out.write(str.getBytes()); //5.關閉資源 client.close(); } }
2、 服務端
查看服務端類ServerSocket的相關方法:
構造函數
ServerSocket
()創建非綁定服務器套接字。
ServerSocket
(int port)創建綁定到特定端口的服務器套接字。
2) 我們會查看ServerSocket類中什麽方法返回InputStream類型,發現並沒有,這是為什麽?
因為TCP建立的是雙向通道,假如有多個客戶端給服務端發送數據,那麽服務端給客戶端返回數據的時候怎麽去處理,一個可能的辦法根據輸入的數據,獲取到它IP、端口,然後再去創建服務端?似乎行不的通。其實服務端socket可以獲取到輸入流,那麽我們在服務端如果能返回一個客戶端的Socket對象,這個Socket與客戶端形成一個輸入輸出的對應通道(理解很牽強)
所以就有了下面的方法:
f
Socketaccept
()
偵聽並接受到此套接字的連接。
關閉套接字
voidclose
()
關閉此套接字。
服務端的建立思路
1) 建立服務端socket。
2) 通過accept()返回客戶端的socket對象。
3) 通過socket對象返回輸入流。
4) 通過IO讀取其中的信息。
5) 關閉資源。
import java.net.*; import java.io.*; class TCPServerDemo { public static voidmain (String[] args) throws SocketException,IOException { //1.建立socekt服務,監聽端口100001 ServerSocketserver = new ServerSocket(10001); //2.返回socekt對象 Socketsocket = server.accept(); //3.獲取輸入流,轉為緩存字符流 InputStreamin = socket.getInputStream(); BufferedReaderbufIn = new BufferedReader(new InputStreamReader(in)); //4.獲取傳輸信息並打印 String info= bufIn.readLine(); System.out.println(info); //關閉資源 server.close(); } }
客戶端與服務端的交互
import java.net.*; import java.io.*; class TCPClientDemo { publicstatic void main(String[] args) throwsSocketException,IOException{ //1.建立socekt服務 Socketclient = new Socket(InetAddress.getLocalHost(),10002); //2.返回輸出數據流 OutputStreamout = client.getOutputStream(); //PrintWriterpwOut = new PrintWriter(out,true); //3.創建傳輸數據 Stringstr = "tcp客戶端:我來了!"+"\r\n"; //4.通過IO流的wirte()方法寫入傳輸數據 out.write(str.getBytes()); //pwOut.println(str); //5.獲取輸入流,轉為緩存字符流 InputStreamin = client.getInputStream(); BufferedReaderbufIn = new BufferedReader(new InputStreamReader(in)); //4.獲取服務端傳輸信息並打印 Stringinfo = bufIn.readLine(); System.out.println(info); //5.關閉資源 client.close(); } }
import java.net.*; import java.io.*; class TCPServerDemo { publicstatic void main (String[] args) throws SocketException,IOException { //1.建立socekt服務,監聽端口100001 ServerSocketserver = new ServerSocket(10002); //2.返回socekt對象 Socketsocket = server.accept(); //3.獲取輸入流,轉為緩存字符流 InputStreamin = socket.getInputStream(); BufferedReaderbufIn = new BufferedReader(new InputStreamReader(in)); //4.獲取傳輸信息並打印 Stringinfo = bufIn.readLine(); System.out.println(info); //5.獲取socket的輸出流,寫入信息 OutputStreamout = socket.getOutputStream(); Stringstr = "服務端:收到信息"+”\r\n”; out.write(str.getBytes()); //6.關閉資源 socket.close(); server.close(); } }
在使用readLine()一定要確認接收的信息中存在回車或者換行,否則會造成阻塞的情況。
練習1:客戶端輸入字母,服務端打印出來,然後把它轉換為大寫字母返回給客戶端。
import java.net.*; import java.io.*; class TransClient { publicstatic void main(String[] args) throws IOException,UnknownHostException{ Sockets = new Socket("127.0.0.1",10001); BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in)); PrintWriterout = new PrintWriter(s.getOutputStream(),true); BufferedReaderbufIn = new BufferedReader(new InputStreamReader(s.getInputStream())); Stringline = null; while((line=bufr.readLine())!=null){ if("over".equals(line)){ break; } out.println(line); StringupperStr = bufIn.readLine(); System.out.println(upperStr); } s.close(); } }
import java.net.*; import java.io.*; class TransServer { publicstatic void main(String[] args) throws IOException,UnknownHostException{ ServerSocketss = new ServerSocket(10001); Sockets = ss.accept(); Stringip = s.getInetAddress().getHostAddress(); System.out.println(ip+"........connected"); BufferedReaderbufIn = new BufferedReader(new InputStreamReader(s.getInputStream())); PrintWriterout = new PrintWriter(s.getOutputStream(),true); Stringline = null; while((line=bufIn.readLine())!=null){ System.out.println(line); out.println(line.toUpperCase()); } s.close(); ss.close(); } }
練習2:上傳一個文本文件。
import java.net.*; import java.io.*; class TextUploadClient { publicstatic void main(String[] agrs) throws IOException,SocketException{ //1.創建soceket服務 Socketclient = new Socket(InetAddress.getLocalHost(),10007); //2.讀取上傳源文件 BufferedReaderbr = new BufferedReader(new FileReader("c:\\client.txt")); //3.獲取socket的輸出流 OutputStreamout = client.getOutputStream(); PrintWriterpw = new PrintWriter(out,true); //4.讀取源文件信息,寫入輸出流。 Stringline = null; while((line=br.readLine())!=null){ //out.write(line.getBytes()); //System.out.println(line); pw.println(line); } client.shutdownOutput(); //5.獲取輸入流,讀取服務端的返回信息 InputStreamin = client.getInputStream(); byte[]buf = new byte[1024]; intlen = in.read(buf); Strings = new String(buf,0,len); System.out.println(s); } }
import java.net.*; import java.io.*; class TextUploadServer { publicstatic void main(String[] agrs) throws IOException,SocketException{ //1.創建soceket服務 ServerSocketserver = new ServerSocket(10007); //2.獲取socket對象 Socketsocket = server.accept(); //3.創建上傳後目的文件 Filefile = new File("c:\\server.txt"); //4.創建目的文件的輸入流 BufferedWriterbw = new BufferedWriter(new FileWriter(file)); //5.獲取socket的輸入流 BufferedReaderbufIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); //6.把socket輸入流中信息寫入文件 Stringline = null; while((line=bufIn.readLine())!=null){ //System.out.println(line); bw.write(line); bw.flush(); bw.newLine(); } //7.獲取socket的輸出流,返回上傳成功的提示 OutputStreamout = socket.getOutputStream(); out.write("上傳成功".getBytes()); //8.關閉資源 bw.close(); socket.close(); server.close(); } }
註意點:上傳文件中的文本通過readLine()讀取到輸入流後,其中文本每一行的回車“\n\r“和文本最後的結束標記不會讀取進去,所以會造成服務端不能換行和readLine()無法結束,需要PrintWriter的println()方法加入換行和給客戶端輸出流加入標記,即socket的shutdownOutput()方法,還有即的緩沖流需要刷新,否則無法及時寫入。
- URL與URLConnection
URL專業術語叫“資源定位符“,意思就是可以指向互聯網的資源,形式其實就是我們常說的網址。 創建URL的對象方法,URL url = new URL(String s);s就是我們的網址。
URLConnetion是URL調用openConnetion返回的,它其實網絡協議和URL地址的封裝,通過它可以完成對URL資源的讀取和寫入等操作。
值得學習的Blog:
http://blog.csdn.net/ns_code/article/details/14128987
http://www.cnblogs.com/oubo/archive/2012/01/16/2394641.html
http://maxuefeng.blog.51cto.com/1876326/438539
Tags: 網絡編程 Java學習 發送信息 臺式電腦 知識點
文章來源: