語義表示要做什么,語法表示要怎么做,時序表示做的順序。
2.網絡OSI七層模型
OSI/RM 模型(Open System Interconnection/Reference Model)。它將計算機網絡體系結構的通信協議划分為七層,自下而上依次為物理層(Physics Layer)、數據鏈路層(Data Link Layer)、網絡層(Network Layer)、傳輸層(Transport Layer)、會話層(Session Layer)、表示層(Presentation Layer)、應用層(Application Layer)。
3.TCP四層模型簡介
自下而上依次為網絡接口層(Network Interface Layer)、網絡層(Network Layer)、傳輸層(Transport Layer)和應用層(Application Layer)。
還可以轉換為五層模型,即將網絡接口層分為物理層和數據鏈路層。
3.1TCP四層作用
物理層規定了物理介質的各種特性,包括機械特性、電子特性、功能特性和規程特性。
數據鏈路層負責接收數據幀並通過網絡發送,或從網絡上接收物理幀再抽離出數據幀交給網絡層。
網絡層管理網絡中的數據通信,設法將數據從源端經過若干中間節點傳送到目的端,從而向傳輸層提供最基本的端到端的數據傳送服務。
傳輸層主要功能包括分割和重組數據並提供差錯控制和流量控制,以達到提供可靠傳輸的目的。為了實現可靠的傳輸,傳輸層協議規定接收端必須發送確認信息以確定數據達到,假如數據丟失,必須重新發送。
應用層對應於 OSI 七層模型的會話層、表示層和應用層,該層向用戶提供一組常用的應用程序服務。
3.2傳輸層的常用協議
1.傳輸控制協議(Transmission Control Protocol,TCP),是一種可靠的面向連接的傳輸服務協議。在 TCP/IP 協議族中,TCP 提供可靠的連接服務,采用“三次握手”建立一個連接。
2.用戶數據報協議(User Datagram Protocol,UDP),是另外一個重要的協議,它提供的是無連接、面向事務的簡單不可靠信息傳送服務。UDP 不提供分割、重組數據和對數據進行排序的功能,也就是說,當數據發送之后,無法得知其是否能安全完整地到達。
UDP是無連接的、不可靠的,資源消耗小,處理速度快,但是在網絡不好的情況下丟包比較嚴重。
3.3應用層的常用協議
-
文件傳輸協議(File Transfer Protocol,FTP),上傳、下載文件可以使用 FTP 服務。
-
Telnet 協議,提供用戶遠程登錄的服務,使用明碼傳送,保密性差,但簡單方便。
-
域名解析服務(Domain Name Service,DNS),提供域名和 IP 地址之間的解析轉換。
-
簡單郵件傳輸協議(Simple Mail Transfer Protocol,SMTP),用來控制郵件的發送、中轉。
-
超文本傳輸協議(Hypertext Transfer Protocol,HTTP),用於實現互聯網中的 WWW 服務。
-
郵局協議的第三個版本(Post Office Protocol 3,POP3),它是規定個人計算機如何連接到互聯網上的郵件服務器進行收發郵件的協議。
3.4數據的封裝與解封
從上向下,數據的傳輸需要加上相應的頭部和尾部,稱為數據的封裝。
從下向上,數據的傳輸需要去掉相應的頭部和尾部,稱為數據的解封。
4 IP地址及其表示
IP地址由兩部分組成:網絡號和主機號
網絡號表示該地址處於哪一個網絡,主機號表示該地址的主機。
IP 地址有兩種表示方式,二進制表示和點分十進制表示,常見的是點分十進制表示的 IP 地址。IP 地址的長度為 32 位,每 8 位組成一個部分,一個 IP 地址可以分為四個部分。如果每個部分用十進制表示,其值的范圍為 0 ~ 255,不同部分之間用“.”分割開來。
5 域名簡介及其分類
域名可分為不同級別,包括頂級域名、二級域名等。
頂級域名又可分為以下兩類:
一類是國家頂級域名,200 多個國家都按照 ISO3166 國家代碼分配了頂級域名,例如中國是 cn,美國是 us,韓國是 kr 等。
另一類是國際頂級域名,一般表示着注冊企業類別的符號,例如表示工商企業的 com,表示網絡提供商的 net,表示非營利組織的 org 等。
二級域名是指頂級域名之下的域名,例如在國際頂級域名下,由域名注冊人申請注冊的網上名稱,例如 sohu、apple、microsoft 等。
6 InetAddress——獲取IP地址
-
InetAddress[] getAllByName(String host)
:通過主機名和配置名返回IP地址。 -
InetAddress getByAddress(byte[] addr)
:通過 IP 地址數組返回InetAddress
對象。 -
InetAddress getByAddress(String host, byte[] addr)
:根據提供的主機名 host 和字節數組形式的 IP 地址 addr,創建InetAddress
對象。 -
InetAddress getByName(String host)
:給定主機名 host,返回InetAddress
對象。 -
InetAddress getLocalHost()
:返回本地主機InetAddress
對象。
InetAddress
類的其他常用方法有以下幾種:
-
byte[] getAddress()
:返回此InetAddress
對象的 IP 地址數組。 -
String getCanonicalHostName()
:返回此 IP 地址的完全限定域名。完全限定域名是指主機名加上全路徑,全路徑中列出了序列中所有域成員。 -
String getHostAddress()
:返回 IP 地址字符串。 -
String getHostName()
:返回此 IP 地址的主機名。
7 URL類——獲取網絡資源的位置
構造方法:URL(地址)
獲取頁面的輸入字節流:url.openStream()
使用IO方法對頁面的內容進行獲取和解析
8 URLConnection類——連接通信
1.使用url.openConnection()獲取連接對象。
2.設置參數和一般請求屬性。
3.使用connect()方法進行遠程連接。
8.1 URLConnection的具體屬性
-
boolean doInput
:將doInput
標志設置為 true,指示應用程序要從 URL 連接讀取數據,此屬性的默認值為 true。此屬性由setDoInput()
方法設置,其值由getDoInput()
方法返回。 -
boolean doOutput
:將doOutput
標志設置為 true,指示應用程序要將數據寫入 URL 連接,此屬性的默認值為 false。此屬性由setDoOutput()
方法設置,其值由getDoOutput()
方法返回。 -
boolean useCaches
:如果其值為 true,則只要有條件就允許協議使用緩存;如果其值為 false,則該協議始終必須獲得此對象的新副本,其默認值為上一次調用setDefaultUseCaches()
方法時給定的值。此屬性由setUseCaches()
方法設置,其值由getUseCaches()
方法返回。 -
boolean connected
:表示URL是否連接成功 -
URL url
:表示Connection類在網上打開的url對象。
9 使用Socket編程之TCP Socket
Socket:套接字,用於端到端的通信。
ServerSocket:用於服務端對象的創建。服務器會初始化一個端口號的Socket,監聽此端口的連接。如果客戶端建立連接,會分配一個帶有新的端口號的Socket,用來和客戶端對話。當連接結束時,會關閉Socket。
ServerSocket的accept()方法:ServerSocket的accept()方法從連接請求隊列中取出一個客戶的連接請求,然后創建與客戶連接的Socket對象,並將它返回。如果隊列中沒有連接請求,accept()方法就會一直等待,直到接收到了連接請求才返回。
服務器為請求連接的客戶進程建立一個先進先出隊列,默認大小一般是50,如果調用accept()方法就會從隊列中取出連接請求,服務端為其建立一個socket。如果隊列已經滿,服務器會拒絕新的連接請求。
9.1使用Socket創建CS連接
package two; import java.io.DataInputStream; import java.io.IOException; import java.net.ConnectException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; public class Test7 { } class TestServer{ public static void main(String[] args) { ServerSocket socket = null; try { //創建服務端的socket socket = new ServerSocket(8888); //創建一個socket,接受客戶端的套接字 Socket s = socket.accept(); //顯示 System.out.println("客戶端的IP:"+s.getInetAddress()+",客戶端的端口號:"+s.getPort()); s.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } class TestClient{ public static void main(String[] args) { try { //服務端和客戶端在同一局域網內。服務端的端口號是8888 Socket socket = new Socket("127.0.0.1",8888); //從socket中獲得客戶端的端口號和IP DataInputStream dis = new DataInputStream(socket.getInputStream()); if(dis.available()!=0)System.out.println(dis.readUTF()); dis.close(); socket.close(); }catch (ConnectException e) { e.printStackTrace(); System.err.println("服務器連接失敗!"); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
9.2使用socket進行圖片上傳
package org.lanqiao.service; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class UploadService { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(10203); Socket socket = server.accept(); //獲取對客戶端寫入的數據輸出字節流 DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); File file = new File("timg.jpg"); //從本地圖片獲得輸入流 FileInputStream fis = new FileInputStream(file); //寫入輸出流 dos.write(fis.readAllBytes()); dos.close(); fis.close(); socket.close(); server.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } package org.lanqiao.client; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.Socket; public class UploadClient { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 10203); //獲取服務端發來的輸入流 DataInputStream dis = new DataInputStream(socket.getInputStream()); File file = new File("pic/mn.jpg"); FileOutputStream fos = new FileOutputStream(file); //將圖片數據寫入本地文件 fos.write(dis.readAllBytes()); System.out.println("上傳完成"); fos.close(); dis.close(); socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
9.3 使用多線程優化CS聊天室
package two; import jdk.net.Sockets; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class Test8 { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(8888); while (true) { //只要服務端開啟,就一直接受客戶端的socket //ServerSocket的accept()方法從連接請求隊列中取出一個客戶的連接請求,然后創建與客戶連接的Socket對象,並將它返回。如果隊列中沒有連接請求,accept()方法就會一直等待,直到接收到了連接請求才返回。 //socket中存儲的是客戶端的ip地址和向客戶端進行通信的端口號 Socket socket = server.accept(); //並為這個socket啟動新的服務端線程 ServerThread st = new ServerThread(socket); st.start(); } } catch (IOException e) { e.printStackTrace(); } } } class ServerThread extends Thread { Socket socket; Scanner sc = new Scanner(System.in); public ServerThread(Socket socket) { this.socket = socket; } @Override public void run() { InputStream is = null; OutputStream os = null; try { //獲取這個socket的輸入輸出流 is = socket.getInputStream(); os = socket.getOutputStream(); DataInputStream dis = new DataInputStream(is); DataOutputStream dos = new DataOutputStream(os); String str = null; while (true) { //一直通信,直到客戶端斷開連接拋出異常 if ((str = dis.readUTF()) != null) { if (str.equals("e")) break; System.out.println("客戶端發來的消息:" + str); } System.out.println("請輸入要向客戶端發送的消息:"); String msg = sc.next(); dos.writeUTF(msg); System.out.println(); } dos.close(); dis.close(); socket.close(); //EOFException表示意外到達流的結尾,如流中斷 } catch (EOFException e) { System.out.println("客戶端" + socket.getInetAddress().getHostAddress() + ":" + socket.getPort() + "退出!"); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } class ClientThread extends Thread { public static void main(String[] args) { Scanner sc = new Scanner(System.in); try { //建立連接,獲取socket //socket中存儲的是服務端的ip地址和向服務端進行通信的端口號 Socket socket = new Socket("127.0.0.1", 8888); InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); DataInputStream dis = new DataInputStream(is); DataOutputStream dos = new DataOutputStream(os); while (true) { //一直通信,直到用戶輸入退出連接 if (dis.available() != 0) System.out.println("服務器發來消息:" + dis.readUTF()); System.out.println("請輸入要向服務器發送的消息(發送e結束):"); String msg = sc.next(); if (msg.equals("e")) { System.out.println("已退出聊天"); break; } dos.writeUTF(msg); } dos.close(); dis.close(); os.close(); is.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
10使用Socket編程之UDP Socket
DatagramSocket類:接受和發送數據報的套接字。使用UDP廣播發送
DatagramPacket類:此類表示數據報包,它用來實現無連接包投遞服務。根據該包中包含的地址和端口等信息,將報文從一台機器路由到另一台機器。
DatagramPacket的構造函數:
DatagramPacket(byte[] buf,int readLength):用於構建接受信息的數據報包
DatagramPacket(byte[] buf,int readLength,InetAddress inet):用於構建發送信息的數據報包,inet對象中需要指定IP和端口號
10.1使用UDP實現客戶端向服務端發送消息
解決中文輸入輸出亂碼:在發送端創建數據報包時,不要直接使用字符串或者字符串.getBytes()獲得的字節數組作為數據。創建ByteArrayOutputStream和以baos為輸出流的DataOutputStream,使用dos的writeUTF()方法,再獲得baos使用的字節數組。將這個數組作為參數。
在接收端創建接受數據包時,不要直接顯示buf創建的字符串,先創建以buf為輸入的ByteArrayInputStream和以bais為輸入流的DataInputStream,最后使用dis的readUTF()讀出這個字節數組。這樣就會識別中文、韓文等語言。
package two; import java.io.*; import java.net.*; import java.util.Scanner; public class Test10 { } class UDPClient { public static void main(String[] args) { Scanner sc = new Scanner(System.in); try { DatagramSocket ds = new DatagramSocket(9999); System.out.println("客戶端:"); while (true) { String line = sc.next(); if (line.equals("bye")) break; //創建baos和dos將讀入數據輸出到字節數組中 ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF(line); byte[] buf = baos.toByteArray(); DatagramPacket packet = new DatagramPacket(buf,buf.length, new InetSocketAddress("127.0.0.1", 8888)); //datagramsocket.send(datagrampacket)方法發送數據報 ds.send(packet); } ds.close(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } class UDPServer { public static void main(String[] args) { try { //接收端socket注冊端口號 DatagramSocket ds = new DatagramSocket(8888); System.out.println("服務器端:"); while (true) { byte[] buf = new byte[1024]; //獲取接收端數據報 DatagramPacket packet = new DatagramPacket(buf, buf.length); //datagramsocket.receive(datagrampacket)方法接受數據報 ds.receive(packet); //packet.getData()獲取數據信息,返回的是字節數組,需要根據數組的長度構建字符串 //使用bais和dis讀取獲得的字節數組。 ByteArrayInputStream bais = new ByteArrayInputStream(buf); DataInputStream dis = new DataInputStream(bais); System.out.println(dis.readUTF()); } } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }