html
程序員
網絡通訊協議:位於同一個網絡中的計算機在進行鏈接和通訊時須要遵照必定的規則,它對數據的傳輸格式、傳輸速率、傳輸步驟等作了統一規定編程
TCP/IP協議:它定義了計算機如何連入因特網,以及數據如何在它們之間傳輸的標準。它的內部包含一系列的用於處理數據通訊的協議,每一層都呼叫它的下一層所提供的協議來完成本身的需求瀏覽器
安全
表示層:數據的表示、安全、壓縮。格式有:JPEG、ASCll、DECOIC、加密格式等。服務器
會話層:創建、管理、終止會話。對應主機進程,指本地主機與遠程主機正在進行的會話網絡
傳輸層:定義傳輸數據的協議端口號,以及流控和差錯校驗。協議有:TCP、UDP。socket
網絡層:進行邏輯地址尋址,實現不一樣網絡之間的路徑選擇。協議有:ICMP、IGMP、IP(IPV4 IPV6)、ARP、RARP。tcp
數據鏈路層:創建邏輯鏈接、進行硬件地址尋址、差錯校驗等功能。將比特組合成字節進而組合成幀,用MAC地址訪問介質,錯誤發現但不能糾正。函數
物理層:創建、維護、斷開物理鏈接。
IP(internet protocal)又稱爲互聯網協議。IP的責任就是把數據從源傳送到目的地。它在源地址和目的地址之間傳送一種稱之爲數據包的東西,它還提供對數據大小的從新組裝功能,以適應不一樣網絡對包大小的要求。常常與IP協議放在一塊兒的還有TCP(Transmission Control Protocol)協議
IP地址用來給一個網絡中的計算機設備作惟一的編號,
IPv4:32位整數,8位一組最多能夠表示42億個
IPv6:採用128位地址長度,每16個字節一組,分紅8組十六進制數
公網地址( 萬維網使用)和 私有地址( 局域網使用)。192.168.開頭的就是私有址址,範圍即爲192.168.0.0--192.168.255.255,專門爲組織機構內部使用
特殊的IP地址:
主機名(hostName):localhost
域名:域名服務器(DNS)負責將域名轉化成IP地址,方便記憶。
若是端口號被另一個服務或應用所佔用,會致使當前程序啓動失敗。
利用協議
+IP地址
+端口號
三元組合,就能夠標識網絡中的進程了,那麼進程間的通訊就能夠利用這個標識與其它進程進行交互。
InetAddress類主要表示IP地址,兩個子類:Inet4Address、Inet6Address。
lInetAddress 類沒有提供公共的構造器,而是提供 了 以下幾個 靜態方法來獲取InetAddress 實例
public static InetAddress getLocalHost() 【返回本地主機】
public static InetAddress getByName(String host) 【在給定主機名的狀況下肯定主機的 IP 地址】
public static InetAddress getByAddress(byte[] addr) 【在給定原始 IP 地址的狀況下,返回 InetAddress
對象】
InetAddress 提供了以下幾個經常使用的方法
public String getHostAddress() : 【返回 IP 地址字符串(以文本表現形式)】
public String getHostName() : 【獲取此 IP 地址的主機名】
socket是兩個主機通訊的關鍵,先理解IO流的工做流程有助於理解網絡間的通訊,socket數據的發送與接收也可簡單的理解爲:
客戶端將要發送的數據經過send()發送給客戶端的tcp/udp協議的緩衝區,由客戶端協議發送給服務端的tcp/udp協議,服務端的receive()會讀取服務端的協議緩衝區接收到的數據,
如需返回數據再經服務端的send()發送給服務端的協議緩衝區,服務端的協議再發送給客戶端的協議,客戶端的receive()會讀取客戶端協議緩衝區中的數據如此循環,直到close()
Socket:此類實現客戶端套接字(也能夠就叫「套接字」)。套接字是兩臺機器間通訊的端點。流套接字
調用 accept() :監聽鏈接請求,若是客戶端請求鏈接,則接受鏈接,返回通訊套接字對象。
調用 該Socket 類對象的 getOutputStream() 和 getInputStream () :獲取輸出流和輸入流,開始網絡數據的發送和接收。
關閉Socket 對象:客戶端訪問結束,關閉通訊套接字。
打開鏈接到 Socket 的輸入/出流: 使用 getInputStream()方法得到輸入流,使用getOutputStream()方法得到輸出流,進行數據傳輸
按照必定的協議對 Socket 進行讀/ 寫操做:經過輸入流讀取服務器放入線路的信息(但不能讀取本身放入線路的信息),經過輸出流將信息寫入線路。
關閉 Socket:斷開客戶端到服務器的鏈接,釋放線路
ServerSocket類的構造方法:
ServerSocket(int port) : 【建立綁定到特定端口的服務器套接字】
ServerSocket類的經常使用方法:
Socket類的經常使用構造方法:
public Socket(InetAddress address,int port): 【建立一個流套接字並將其鏈接到指定 IP 地址的指定端口號】
Socket類的經常使用方法:
public InputStream getInputStream(): 【返回此套接字的輸入流,能夠用於接收消息】
public OutputStream getOutputStream(): 【返回此套接字的輸出流,能夠用於發送消息】
public InetAddress getInetAddress(): 【返回此套接字鏈接到的遠程 IP 地址;若是套接字是未鏈接的,則返回 null】
public InetAddress getLocalAddress(): 【獲取套接字綁定的本地地址】
public int getPort(): 【返回此套接字鏈接到的遠程端口號;若是還沒有鏈接套接字,則返回 0】
public int getLocalPort(): 【返回此套接字綁定到的本地端口。若是還沒有綁定套接字,則返回 -1】
public void close(): 【關閉套接字(即沒法從新鏈接或從新綁定) 同時也將會關閉該套接字的 InputStream 和 OutputStream】
public void shutdownInput():
若是在套接字上調用 shutdownInput() 後從套接字輸入流讀取內容,則流將返回 EOF(文件結束符)。 即不能在今後套接字的輸入流中接收任何數據。關閉輸入流
public void shutdownOutput():
禁用此套接字的輸出流。對於 TCP 套接字,任何之前寫入的數據都將被髮送,而且後跟 TCP 的正常鏈接終止序列。 若是在套接字上調用 shutdownOutput() 後寫入套接字輸出流,則該流將拋出 IOException。 即不能經過此套接字的輸出流發送任何數據。關閉輸出流
注意:前後調用Socket的shutdownInput()和shutdownOutput()方法,僅僅關閉了輸入流和輸出流,並不等於調用Socket的close()方法。在通訊結束後,仍然要調用Scoket的close()方法,由於只有該方法纔會釋放Socket佔用的資源,好比佔用的本地端口號等。
public DatagramSocket(int port)
建立數據報套接字並將其綁定到本地主機上的指定端口。套接字將被綁定到通配符地址,IP 地址由內核來選擇。
public DatagramSocket(int port,InetAddress laddr)
建立數據報套接字,將其綁定到指定的本地地址。本地端口必須在 0 到 65535 之間(包括二者)。若是 IP 地址爲 0.0.0.0,套接字將被綁定到通配符地址,IP 地址由內核選擇。
public void send(DatagramPacket p)
今後套接字發送數據報包。DatagramPacket 包含的信息指示:將要發送的數據、其長度、遠程主機的 IP 地址和遠程主機的端口號。
public void receive(DatagramPacket p)
今後套接字接收數據報包。當此方法返回時,DatagramPacket 的緩衝區填充了接收的數據。數據報包也包含發送方的 IP 地址和發送方機器上的端口號。 此方法在接收到數據報前一直阻塞。數據報包對象的 length 字段包含所接收信息的長度。若是信息比包的長度長,該信息將被截短。
DatagramPacket類的經常使用方法:
public DatagramPacket(byte[] buf,int length)
構造 DatagramPacket,用來接收長度爲 length 的數據包。 length 參數必須小於等於 buf.length。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
構造數據報包,用來將長度爲 length 的包發送到指定主機上的指定端口號。length 參數必須小於等於 buf.length。
public int getLength() 【返回將要發送或接收到的數據的長度】
Datagram只容許數據報發送給指定的目標地址,而MulticastSocket能夠將數據報以廣播方式發送到數量不等的多個客戶端。
IP協議爲多點廣播提供了這批特殊的IP地址,這些IP地址的範圍是224.0.0.0至239.255.255.255。
MulticastSocket經常使用的方法:
MulticastSocket(int port) :
建立多播套接字並將其綁定到特定端口。建立一個MulticastSocket對象後,還須要將該MulticastSocket加入到指定的多點廣播地址,若是結束也須要脫離多點廣播地址。
void joinGroup(InetAddress mcastaddr) :【加入多播組。】
void leaveGroup(InetAddress mcastaddr) :【離開多播組。】
void setLoopbackMode(boolean disable) :【啓用/禁用多播數據報的本地回送。true 表示禁用LoopbackMode。】
public class Client { public static void main(String[] args) throws UnknownHostException, IOException { // 一、鏈接服務器 Socket socket = new Socket("127.0.0.1", 9999); // 二、開啓兩個線程,一個收消息,一個發消息 SendThread st = new SendThread(socket); ReceiveThread rt = new ReceiveThread(socket);
st.start(); rt.start(); // 等發送線程停下來再往下走 try { st.join(); } catch (InterruptedException e) { e.printStackTrace(); } // 等接收線程停下來,再往下走,斷開鏈接 try { rt.join(); } catch (InterruptedException e) { e.printStackTrace(); } socket.close(); } static class SendThread extends Thread { private Socket socket; public SendThread(Socket socket) { super(); this.socket = socket; } public void run() { try { // 鍵盤輸入 Scanner input = new Scanner(System.in); OutputStream out = socket.getOutputStream(); PrintStream ps = new PrintStream(out); while (true) { // 從鍵盤輸入 System.out.print("請輸入要發送的消息:"); String content = input.nextLine(); // 給服務器發送 ps.println(content); // 若是bye,就結束髮送 if ("bye".equals(content)) { break; } } input.close(); } catch (IOException e) { e.printStackTrace(); } } } static class ReceiveThread extends Thread { private Socket socket; public ReceiveThread(Socket socket) { super(); this.socket = socket; } public void run() { try { InputStream in = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr); while (true) { String line = br.readLine(); if("bye".equals(line)){ break; } System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } } }
public class Server { private static ArrayList<Socket> online = new ArrayList<Socket>(); public static void main(String[] args) throws IOException { //一、開啓服務器 ServerSocket server = new ServerSocket(9999); while(true){ //二、接收客戶端的鏈接 Socket socket = server.accept(); //把這個客戶端加入到online中 online.add(socket); //每個客戶端獨立的線程 MessageHandler mh = new MessageHandler(socket); mh.start(); } } private static class MessageHandler extends Thread{ private Socket socket; private String ip; public MessageHandler(Socket socket) { super(); this.socket = socket; this.ip = socket.getInetAddress().getHostAddress(); } public void run(){ //這個客戶端的一鏈接成功,線程一啓動,就能夠告訴其餘人我上線了 sendToOthers(ip+"上線了"); //(1)接收當前的客戶端發送的消息 try { InputStream in = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr); String content; while((content = br.readLine()) !=null){ //收到一句,轉發一句 sendToOthers(ip+"說:" + content); if("bye".equals(content)){ //給本身發一句bye OutputStream out = socket.getOutputStream(); PrintStream ps = new PrintStream(out); ps.println("bye"); break; } } sendToOthers(ip+"下線了"); } catch (IOException e) { sendToOthers(ip+"掉線了"); } } //由於轉發的代碼也很長,獨立爲一個方法 public void sendToOthers(String str){ //遍歷全部online的客戶端 Iterator<Socket> iterator = online.iterator(); while(iterator.hasNext()){ Socket on = iterator.next(); if(!on.equals(socket)){//只給其餘客戶端轉發 try { OutputStream out = on.getOutputStream(); PrintStream ps = new PrintStream(out); ps.println(str); } catch (IOException e) { //說明on這個客戶端要麼下線了,要麼掉線了 iterator.remove(); } } } } } }