## Java網絡編程
### java網絡編程基礎知識
### 1、協議(TCP/IP)
????TCP/IP(Transmission?Control?Protocol/Internet?Protocol)的簡寫,中文譯名為傳輸控制協議/因特網互聯協議,又叫網絡通訊協議,這個協議是Internet最基本的協議、Internet國際互聯網絡的基礎,簡單地說,就是由網絡層的IP協議和傳輸層的TCP協議組成的。
### TCP/IP協議的由來
????在阿帕網(ARPR)產生運作之初,通過接口信號處理機實現互聯的電腦并不多,大部分電腦相互之間不兼容,在一臺電腦上完成的工作,很難拿到另一臺電腦上去用,想讓硬件和軟件都不一樣的電腦聯網,也有很多困難。當時美國的狀況是,陸軍用的電腦是DEC系列產品,海軍用的電腦是Honeywell中標機器,空軍用的是IBM公司中標的電腦,每一個軍種的電腦在各自的系里都運行良好,但卻有一個大弊病:不能共享資源。
?
### 互聯網之父--瑟夫(Vinton?G.Cerf)
1997年,為了褒獎對因特網發展作出突出貢獻的科學家,并對TCP/IP協議作出充分肯定,美國授予為因特網發明和定義TCP/IP協議的文頓·瑟夫和卡恩“國家技術金獎”。這無疑使人們認識到TCP/IP協議的重要性。

### tcp/ip基礎--ip地址與包的路由傳遞
ip地址
概述:每個internet上的主機和路由器都有一個ip地址,它包括網絡號和主機號,所有ip地址都是32位的,ip地址按照國際標準的劃分為a,b,c,d,e五種類型。
?
### 端口(port)--概念
????在網絡技術中,端口(Port)有好幾種意思。集線器、交換機、路由器的端口指的是連接其他網絡設備的接口,如RJ-45端口、Serial端口等。這里所指的端口不是指物理意義上的端口,而是特指TCP/IP協議中的端口,是邏輯意義上的端口。
????如果把IP地址比作一間房子,端口就是出入這間房子的門。真正的房子只有幾個門,但是一個IP地址的端口可以有65536(即:256*256)個之多!端口是通過端口號來標記的,端口號只有整數,范圍是從0到65535(256*256-1)。
?
### 端口(port)--分類
有65536個端口0號是保留端口
1-1024是固定端口
又叫有名端口,即被某些程序固定使用,一般程序員不使用。
22:SSH遠程登錄協議 23:telnet使用 21:ftp使用
25:smtp服務使用 80:iis使用 7:echo服務
1025-65535是動態端口
這些端口,程序員可以使用
?
### 端口(port)--注意事項
1、在計算機(尤其是做服務器)要盡可能的少開端口;
2、一個端口只能被一個程序監聽;
3、如果使用netstat?-an可以查看本機有哪些端口在監聽
4、可以使用netstat?-anb來查看監聽端口的pid,在結合任務管理器關閉不需要的端口。
?
### url--概念
????統一資源定位符(URL,Uniform?Resource?Locator的縮寫)也被稱為網頁地址,是因特網上標準的資源的地址。它最初是由蒂姆·伯納斯-李發明用來作為萬維網的地址的。現在它已經被萬維網聯盟編制為因特網標準RFC1738了。
????Internet上的每一個網頁都具有一個唯一的名稱標識,通常稱之為URL地址,這種地址可以是本地磁盤,也可以是局域網上的某一臺計算機,更多的是Internet上的站點,簡單地說,URL就是Web地址,俗稱“網址”,是用于完整地描述Internet上網頁和其他資源的地址的一種標識方法。
?
### url--組成
http://www.sina.com:8080/index.html
1、協議;2、ip地址(32位);3、端口號(16位)0-65535;4、資源名稱。
?
### 單工、半雙工和全雙工
????如果甲可以向乙發送數據,但是乙不能向甲發送數據,這樣的通信就是單工通信(Simplex?Communication)。
????單工數據傳輸只支持數據在一個方向上傳輸,就和傳呼機一樣。
????半雙工數據傳輸允許數據在兩個方向上傳輸,但是,在某一時刻,只允許數據在一個方向上傳輸,它實際上是一種切換方向的單工通信,就和對講機一樣;
全雙工數據通信允許數據同時在兩個方向上傳輸,因此,全雙工通信是兩個單工通信方式的結合,它要求發送設備和接收設備都有獨立的接收和發送能力,就和電話一樣。

####
#### 案例一:半雙工
##### Server1.java
~~~
/**
* 半雙工
* 服務器端,9999端口監聽
* 可以接收從客戶端發來的信息
*/
package com.net.server;
import java.io.IOException;
import java.net.*;
import java.io.*;
public class Server1 {
ServerSocket ss = null;
Socket s = null;
// 構造方法
public Server1() {
try {
// 在9999端口進行監聽
ss = new ServerSocket(9999);
// 等待某個客戶端來連接,該方法會返回一個Socket連接
s = ss.accept();
// 讀取s中傳遞的數據
InputStreamReader isr = new InputStreamReader(s.getInputStream());
BufferedReader br = new BufferedReader(isr);
String info = br.readLine();
System.out.println("服務器接收到了" + info);
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
pw.println("Hello,Client!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (s != null) {
s.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server1 s1 = new Server1();
}
}
~~~
##### Client1.java
~~~
/**
* 客戶端
*/
package com.net.client;
import java.io.IOException;
import java.net.*;
import java.io.*;
public class Client1 {
Socket s = null;
//構造方法
public Client1(){
try {
//Socket()方法可以連接某個服務器,127.0.0.1表示服務器的IP,9999是該服務器的端口號
s = new Socket("127.0.0.1", 9999);
//連接成功,就可以發送數據給服務器
//通過PrintWriter向s寫數據,true表示即時刷新
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
pw.println("Hello,Server!");
InputStreamReader isr = new InputStreamReader(s.getInputStream());
BufferedReader br = new BufferedReader(isr);
String resp = br.readLine();
System.out.println("客戶端接收到了" + resp);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(s != null){
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client1 c1 = new Client1();
}
}
~~~
#### 案例二:半雙工聊天
##### Server2.java
~~~
/**
* 半雙工
* 服務器端,9999端口監聽
* 可以接收從客戶端發來的信息
*/
package com.net.server;
import java.net.*;
import java.io.*;
public class Server2 {
ServerSocket ss = null;
Socket s = null;
BufferedReader br1 = null;
BufferedReader br2 = null;
PrintWriter pw = null;
// 構造方法
public Server2() {
try {
// 在9999端口進行監聽
ss = new ServerSocket(9999);
// 等待某個客戶端連接,返回 Socket
s = ss.accept();
while (true) {
// 從客戶端接收消息
br1 = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String str = br1.readLine();
System.out.println("從客戶端收到的消息是:" + str);
// 如果收到消息為"byebye",關閉服務器
if (str.equals("byebye")) {
System.out.println("服務器關閉連接");
break;
}
// 從控制臺輸入
System.out.print("從控制臺輸入:");
br2 = new BufferedReader(new InputStreamReader(System.in));
// 將消息發送給客戶端
pw = new PrintWriter(s.getOutputStream(), true);
pw.println(br2.readLine());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 關閉資源
if (pw != null) {
pw.close();
}
if (br2 != null) {
br2.close();
}
if (br1 != null) {
br1.close();
}
if (s != null) {
s.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server2 s2 = new Server2();
}
}
~~~
##### Client2.java
~~~
/**
* 客戶端
*/
package com.net.client;
import java.net.*;
import java.io.*;
public class Client2 {
Socket s = null;
PrintWriter pw = null;
BufferedReader br1 = null;
BufferedReader br2 = null;
public Client2() {
try {
// Socket()方法可以連接某個服務器,127.0.0.1表示服務器的IP,9999是該服務器的端口號
s = new Socket("127.0.0.1", 9999);
while (true) {
// 連接成功,就可以發送數據給服務器
// 從控制臺讀入數據
System.out.print("從控制臺輸入:");
br1 = new BufferedReader(new InputStreamReader(System.in));
String str = br1.readLine();
// 通過PrintWriter向s寫數據,true表示即時刷新
pw = new PrintWriter(s.getOutputStream(), true);
pw.println(str);
if (str.equals("byebye")) {
System.out.println("客戶端關閉連接");
break;
}
// 從服務端接收消息
br2 = new BufferedReader(new InputStreamReader(
s.getInputStream()));
System.out.println("從服務器接收的消息是:" + br2.readLine());
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 關閉資源
if (pw != null) {
pw.close();
}
if (br2 != null) {
br2.close();
}
if (br1 != null) {
br1.close();
}
if (s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client2 c2 = new Client2();
}
}
~~~
#### 案例三:全雙工聊天
##### Server3.java
~~~
/**
* 全雙工
* 監聽端口6166
*/
package com.net.server;
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.RenderingHints.Key;
import javax.swing.*;
import java.awt.event.*;
public class Server3 extends JFrame implements ActionListener, KeyListener {
// 網絡變量
ServerSocket ss = null;
Socket s = null;
// IO流
BufferedReader br = null;
PrintWriter pw = null;
// 定義組件
JTextArea jta = null;
JTextField jtf = null;
JButton jb = null;
JPanel jp1 = null;
JScrollPane jsp = null;
// 構造方法
public Server3() {
// 創建組件
jta = new JTextArea();
jsp = new JScrollPane(jta);
jtf = new JTextField(20);
jtf.addKeyListener(this);
jb = new JButton("發送");
jb.addActionListener(this);
jp1 = new JPanel();
// 添加到JPanel
jp1.add(jtf);
jp1.add(jb);
// 添加到JFrame
add(jsp, "Center");
add(jp1, "South");
// 設置窗體
setTitle("服務器端");
setSize(400, 300);
setLocation(0, 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
try {
// 服務器監聽
ss = new ServerSocket(6166);
// 等待客戶端連接
s = ss.accept();
// 創建讀入緩沖字符流
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 創建打印流
pw = new PrintWriter(s.getOutputStream(), true);
while (true) {
// 讀取從客戶端發來的信息
// 當沒有消息過來時,會一直處于阻塞狀態,這條語句也就不會執行
String info = br.readLine();
jta.append("客戶端:" + info + "\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server3 s3 = new Server3();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jb) {
// 點擊確定,發送信息
// 把服務器在jtf寫的內容發送給客戶端,并在jta顯示
String info = jtf.getText();
pw.println(info);
jta.append("服務器:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// 按下Enter,發送信息
// 把服務器在jtf寫的內容發送給客戶端,并在jta顯示
String info = jtf.getText();
pw.println(info);
jta.append("服務器:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
~~~
##### Client3.java
~~~
/**
* 客戶端
*/
package com.net.client;
import java.net.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Client3 extends JFrame implements ActionListener, KeyListener {
// 網絡變量
Socket s = null;
// IO流
BufferedReader br = null;
PrintWriter pw = null;
// 定義組件
JTextArea jta = null;
JTextField jtf = null;
JButton jb = null;
JPanel jp1 = null;
JScrollPane jsp = null;
// 構造方法
public Client3() {
// 創建組件
jta = new JTextArea();
jsp = new JScrollPane(jta);
jtf = new JTextField(20);
jtf.addKeyListener(this);
jb = new JButton("發送");
jb.addActionListener(this);
jp1 = new JPanel();
// 添加到JPanel
jp1.add(jtf);
jp1.add(jb);
// 添加到JFrame
add(jsp, "Center");
add(jp1, "South");
// 設置窗體
setTitle("客戶端");
setSize(400, 300);
setLocation(500, 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
try {
// Socket()方法可以連接某個服務器,127.0.0.1表示服務器的IP,6166是該服務器的端口號
s = new Socket("127.0.0.1", 6166);
// 創建讀入緩沖字符流
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 創建打印流
pw = new PrintWriter(s.getOutputStream(), true);
while (true) {
// 讀取從服務器發來的消息
// 當沒有消息過來時,會一直處于阻塞狀態,這條語句也就不會執行
String info = br.readLine();
jta.append("服務器:" + info + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 關閉資源
if (pw != null) {
pw.close();
}
if (br != null) {
br.close();
}
if (s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client3 c3 = new Client3();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jb) {
// 如果點擊確定按鈕
// 發送給服務端,并更新jtf,并在jta顯示
String info = jtf.getText();
pw.println(info);
jta.append("客戶端:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// 按下Enter,發送信息
// 把服務器在jtf寫的內容發送給客戶端,并在jta顯示
String info = jtf.getText();
pw.println(info);
jta.append("服務器:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
~~~
?
#### 案例四:在網絡間傳遞對象
##### User.java
~~~
/**
* 對象流所用對象
* 必須序列化
*/
package com.objectstream;
import java.io.*;
public class User implements Serializable {
private String name;
private String pass;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
}
~~~
##### Server1.java
~~~
/**
* 對象流
* 服務器
* 監聽端口1234
*/
package com.objectstream;
import java.net.*;
import java.io.*;
public class Server1 {
ServerSocket ss = null;
Socket s = null;
ObjectInputStream ois = null;
// 構造方法
public Server1() {
try {
ss = new ServerSocket(1234);
s = ss.accept();
System.out.println("在1234端口監聽");
// 以對象流方式讀取(假定客戶端發送的是User的一個對象)
ois = new ObjectInputStream(s.getInputStream());
User u = (User) ois.readObject();
// 輸出
System.out.println("從客戶端接收到了" + u.getName() + " " + u.getPass());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
if (s != null) {
s.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server1 s1 = new Server1();
}
}
~~~
##### Client1.java
~~~
/**
* 客戶端
*/
package com.objectstream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client1 {
Socket s = null;
ObjectOutputStream oos = null;
// 構造方法
public Client1() {
try {
s = new Socket("127.0.0.1", 1234);
// 通過ObjectOutputStream給服務器傳遞對象
oos = new ObjectOutputStream(s.getOutputStream());
User u = new User();
u.setName("張三");
u.setPass("123456");
oos.writeObject(u);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
if (s != null) {
s.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client1 c1 = new Client1();
}
}
~~~
特別說明:
在java網絡編程要通過IO流實現對象的傳遞,需要對服務端和客戶端定義相同的對象,以達到實現對象的傳遞。同時需要類進行Serializable序列化,否則報錯。
----------參考《韓順平.循序漸進學.java.從入門到精通》
----------參考《JDK_API_1_6_zh_CN》
Java學習筆記--導航[http://blog.csdn.net/q547550831/article/details/49819641](http://blog.csdn.net/q547550831/article/details/49819641)