文檔版本 | 開發(fā)工具 | 測試平臺(tái) | 工程名字 | 日期 | 作者 | 備注 |
---|---|---|---|---|---|---|
V1.0 | 2016.04.03 | lutianfei | none |
[TOC]
第十二章網(wǎng)絡(luò)編程
網(wǎng)絡(luò)編程概述
- 就是用來實(shí)現(xiàn)網(wǎng)絡(luò)互連的不同計(jì)算機(jī)上運(yùn)行的程序間可以進(jìn)行數(shù)據(jù)交換。
網(wǎng)絡(luò)模型
- 計(jì)算機(jī)網(wǎng)絡(luò)之間以何種規(guī)則進(jìn)行通信诵姜,就是網(wǎng)絡(luò)模型研究問題。
-
網(wǎng)絡(luò)模型
一般是指- OSI(Open System Interconnection開放系統(tǒng)互連)參考模型
- TCP/IP參考模型
網(wǎng)絡(luò)模型7層概述
1 .
物理層
:主要定義物理設(shè)備標(biāo)準(zhǔn)蚌吸,如網(wǎng)線的接口類型赶舆、光纖的接口類型、各種傳輸介質(zhì)的傳輸速率等焙格。它的主要作用是傳輸比特流。這一層的數(shù)據(jù)叫做比特己英。2 .
數(shù)據(jù)鏈路層
:主要將從物理層接收的數(shù)據(jù)進(jìn)行MAC地址(網(wǎng)卡的地址)的封裝
與解封裝
间螟。常把這一層的數(shù)據(jù)叫做幀
。在這一層工作的設(shè)備是交換機(jī)损肛,數(shù)據(jù)通過交換機(jī)來傳輸厢破。3 .
網(wǎng)絡(luò)層
:主要將從下層接收到的數(shù)據(jù)進(jìn)行IP地址的封裝
與解封裝
。在這一層工作的設(shè)備是路由器治拿,常把這一層的數(shù)據(jù)叫做數(shù)據(jù)包
摩泪。-
4 .
傳輸層
:定義了一些傳輸數(shù)據(jù)的協(xié)議和端口號(hào)。 主要是將從下層接收的數(shù)據(jù)進(jìn)行分段和傳輸劫谅,到達(dá)目的地址后再進(jìn)行重組见坑。常常把這一層數(shù)據(jù)叫做段
嚷掠。-
TCP
:傳輸控制協(xié)議,傳輸效率低荞驴,可靠性強(qiáng)不皆,用于傳輸可靠性要求高,數(shù)據(jù)量大的數(shù)據(jù)熊楼。 -
UDP
:用戶數(shù)據(jù)包協(xié)議霹娄,與TCP特性恰恰相反,用于傳輸可靠性要求不高鲫骗,數(shù)據(jù)量小的數(shù)據(jù)犬耻。- 如QQ聊天數(shù)據(jù)就是通過這種方式傳輸?shù)摹?/li>
-
5 .
會(huì)話層
:通過傳輸層建立數(shù)據(jù)傳輸?shù)耐贰V饕谀愕南到y(tǒng)之間發(fā)起會(huì)話或者接受會(huì)話請求(設(shè)備之間需要互相認(rèn)識(shí)可以是IP也可以是MAC或者是主機(jī)名)执泰。6 .
表示層
:主要是進(jìn)行對接收的數(shù)據(jù)進(jìn)行解釋枕磁、加密與解密、壓縮與解壓縮等(把計(jì)算機(jī)能夠識(shí)別的東西轉(zhuǎn)換成人能夠能識(shí)別的東西(如圖片术吝、聲音等)计济。7 .
應(yīng)用層
: 主要是一些終端的應(yīng)用,比如說FTP(各種文件下載)顿苇,WEB(IE瀏覽)峭咒,QQ之類的(可以把它理解成我們在電腦屏幕上可以看到的東西.就是終端應(yīng)用)。
網(wǎng)絡(luò)通信三要素
- IP地址:InetAddress
- 網(wǎng)絡(luò)中設(shè)備的標(biāo)識(shí)纪岁,不易記憶,可用主機(jī)名
- 端口號(hào)
- 用于標(biāo)識(shí)進(jìn)程的邏輯地址则果,不同進(jìn)程的標(biāo)識(shí)
- 傳輸協(xié)議
- 通訊的規(guī)則
- 常見協(xié)議:TCP幔翰,UDP
網(wǎng)絡(luò)通信模型
- 第一個(gè)條件:我要先找到你 (IP)
- 第二個(gè)條件:你得有接收數(shù)據(jù)的地方 耳朵 (端口)
- 第三個(gè)條件:我跟你說話,你能接收到,咱按什么方式接收啊,我說英文你懂嗎,說韓文你懂嗎,不懂是吧,所以我還是說中文把.(協(xié)議)
IP地址
要想讓網(wǎng)絡(luò)中的計(jì)算機(jī)能夠互相通信,必須為每臺(tái)計(jì)算機(jī)指定一個(gè)標(biāo)識(shí)號(hào)西壮,通過這個(gè)標(biāo)識(shí)號(hào)來指定要接受數(shù)據(jù)的計(jì)算機(jī)和識(shí)別發(fā)送的計(jì)算機(jī)遗增,在TCP/IP協(xié)議中,這個(gè)標(biāo)識(shí)號(hào)就是IP地址款青。
-
如何獲取和操作IP地址呢做修?
- 為了方便我們對IP地址的獲取和操作,java提供了一個(gè)類
InetAddress
抡草。
- 為了方便我們對IP地址的獲取和操作,java提供了一個(gè)類
所謂IP地址就是給每個(gè)連接在Internet上的主機(jī)分配的一個(gè)32bit地址饰及。按照TCP/IP規(guī)定,IP地址用二進(jìn)制來表示康震,每個(gè)IP地址長32bit燎含,比特?fù)Q算成字節(jié),就是4個(gè)字節(jié)腿短。IP地址的這種表示法叫做“
點(diǎn)分十進(jìn)制表示法
”屏箍,這顯然比1和0容易記憶得多绘梦。
IP地址的組成
IP地址 =
網(wǎng)絡(luò)號(hào)碼
+主機(jī)地址
A類IP地址
: 第一段號(hào)碼為網(wǎng)絡(luò)號(hào)碼
,剩下的三段號(hào)碼為本地計(jì)算機(jī)的號(hào)碼
B類IP地址
:前二段號(hào)碼為網(wǎng)絡(luò)號(hào)碼
赴魁,剩下的二段號(hào)碼為本地計(jì)算機(jī)的號(hào)碼
C類IP地址
:前三段號(hào)碼為網(wǎng)絡(luò)號(hào)碼
卸奉,剩下的一段號(hào)碼為本地計(jì)算機(jī)的號(hào)碼
-
特殊地址:
-
127.0.0.1
回環(huán)地址
,可用于測試本機(jī)的網(wǎng)絡(luò)是否有問題。- eg : ping 127.0.0.1
- xxx.xxx.xxx.0 網(wǎng)絡(luò)地址
- xxx.xxx.xxx.255 廣播地址
-
127.0.0.1
InetAddress類的使用
- 沒有構(gòu)造方法颖御,那么如何使類提供的功能呢择卦?
- 要掌握的方法
- 獲取任意主機(jī):getByName
- 主機(jī)名:getHostName
- 主機(jī)Ip地址:getHostAddress
端口號(hào)
- 物理端口 網(wǎng)卡口
- 邏輯端口 我們指的就是邏輯端口
- A:每個(gè)網(wǎng)絡(luò)程序都會(huì)至少有一個(gè)邏輯端口
- B:用于標(biāo)識(shí)進(jìn)程的邏輯地址,不同進(jìn)程的標(biāo)識(shí)
- C:有效端口:065535郎嫁,其中01024系統(tǒng)使用或保留端口秉继。
- 通過360可以查看端口號(hào)
協(xié)議UDP和TCP
UDP
- 將數(shù)據(jù)源和目的封裝成數(shù)據(jù)包中,不需要建立連接泽铛;每個(gè)數(shù)據(jù)報(bào)的大小在限制在64k尚辑;因無連接,是不可靠協(xié)議盔腔;不需要建立連接杠茬,速度快
- 舉例:
- 聊天留言,在線視頻弛随,視頻會(huì)議瓢喉,發(fā)短信,郵局包裹舀透。
TCP
- 建立連接栓票,形成傳輸數(shù)據(jù)的通道;在連接中進(jìn)行大數(shù)據(jù)量傳輸愕够;通過三次握手完成連接走贪,是可靠協(xié)議;必須建立連接惑芭,效率會(huì)稍低
- 舉例:
- 下載坠狡,打電話,QQ聊天(你在線嗎,在線,就回應(yīng)下,就開始聊天了)
Socket編程
- 又稱
網(wǎng)絡(luò)編程
遂跟,套接字編程
- Socket套接字:
- 網(wǎng)絡(luò)上具有唯一標(biāo)識(shí)的
IP地址
和端口號(hào)
組合在一起才能構(gòu)成唯一能識(shí)別的標(biāo)識(shí)符套接字逃沿。
- 網(wǎng)絡(luò)上具有唯一標(biāo)識(shí)的
- Socket原理機(jī)制:
- 通信的兩端都有Socket。
- 網(wǎng)絡(luò)通信其實(shí)就是Socket間的通信幻锁。
- 數(shù)據(jù)在兩個(gè)Socket間通過IO傳輸凯亮。
UDP傳輸過程
-
DatagramSocket
與DatagramPacket
- 建立發(fā)送端,接收端越败。
- 建立數(shù)據(jù)包触幼。
- 調(diào)用Socket的發(fā)送接收方法。
- 關(guān)閉Socket究飞。
- 發(fā)送端與接收端是兩個(gè)獨(dú)立的運(yùn)行程序置谦。
UDP傳輸-發(fā)送端思路
1:建立udp的socket服務(wù)
2:將要發(fā)送的數(shù)據(jù)封裝成數(shù)據(jù)包
3:通過udp的socket服務(wù),將數(shù)據(jù)包發(fā)送出
4:關(guān)閉資源
UDP傳輸-發(fā)送端代碼
/*
* UDP協(xié)議發(fā)送數(shù)據(jù):
* A:創(chuàng)建發(fā)送端Socket對象
* B:創(chuàng)建數(shù)據(jù)堂鲤,并把數(shù)據(jù)打包
* C:調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
* D:釋放資源
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建發(fā)送端Socket對象
// DatagramSocket()
DatagramSocket ds = new DatagramSocket();
// 創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包
// DatagramPacket(byte[] buf, int length, InetAddress address, int port)
// 創(chuàng)建數(shù)據(jù)
byte[] bys = "hello,udp,我來了".getBytes();
// 長度
int length = bys.length;
// IP地址對象
InetAddress address = InetAddress.getByName("192.168.12.92");
// 端口
int port = 10086;
DatagramPacket dp = new DatagramPacket(bys, length, address, port);
// 調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
// public void send(DatagramPacket p)
ds.send(dp);
// 釋放資源
ds.close();
}
}
UDP傳輸-接收端思路
1:建立udp的socket服務(wù).
2:通過receive方法接收數(shù)據(jù)
3:將收到的數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)包對象中
4:通過數(shù)據(jù)包對象的功能來完成對接收到數(shù)據(jù)進(jìn)行解析.
5:可以對資源進(jìn)行關(guān)閉
UDP傳輸-接收端代碼
/*
* UDP協(xié)議接收數(shù)據(jù):
* A:創(chuàng)建接收端Socket對象
* B:創(chuàng)建一個(gè)數(shù)據(jù)包(接收容器)
* C:調(diào)用Socket對象的接收方法接收數(shù)據(jù)
* D:解析數(shù)據(jù)包媒峡,并顯示在控制臺(tái)
* E:釋放資源
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建接收端Socket對象
// DatagramSocket(int port)
DatagramSocket ds = new DatagramSocket(10086);
// 創(chuàng)建一個(gè)數(shù)據(jù)包(接收容器)
// DatagramPacket(byte[] buf, int length)
byte[] bys = new byte[1024];
int length = bys.length;
DatagramPacket dp = new DatagramPacket(bys, length);
// 調(diào)用Socket對象的接收方法接收數(shù)據(jù)
// public void receive(DatagramPacket p)
ds.receive(dp); // 阻塞式
// 解析數(shù)據(jù)包瘟栖,并顯示在控制臺(tái)
// 獲取對方的ip
// public InetAddress getAddress()
InetAddress address = dp.getAddress();
String ip = address.getHostAddress();
// public byte[] getData():獲取數(shù)據(jù)緩沖區(qū)
// public int getLength():獲取數(shù)據(jù)的實(shí)際長度
byte[] bys2 = dp.getData();
int len = dp.getLength();
String s = new String(bys2, 0, len);
System.out.println(ip + "傳遞的數(shù)據(jù)是:" + s);
// 釋放資源
ds.close();
}
}
- 優(yōu)化后代碼
/*
* 多次啟動(dòng)接收端:
* java.net.BindException: Address already in use: Cannot bind
* 端口被占用。
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建接收端的Socket對象
DatagramSocket ds = new DatagramSocket(12345);
// 創(chuàng)建一個(gè)包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收數(shù)據(jù)
ds.receive(dp);
// 解析數(shù)據(jù)
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println("from " + ip + " data is : " + s);
// 釋放資源
ds.close();
}
}
public class SendDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建發(fā)送端的Socket對象
DatagramSocket ds = new DatagramSocket();
// 創(chuàng)建數(shù)據(jù)并打包
byte[] bys = "helloworld".getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("192.168.12.92"), 12345);
// 發(fā)送數(shù)據(jù)
ds.send(dp);
// 釋放資源
ds.close();
}
}
UDP案例
- 從鍵盤錄入數(shù)據(jù)進(jìn)行發(fā)送谅阿,如果輸入的是886那么客戶端就結(jié)束輸入數(shù)據(jù)半哟。
/*
* 多次啟動(dòng)接收端:
* java.net.BindException: Address already in use: Cannot bind
* 端口被占用。
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建接收端的Socket對象
DatagramSocket ds = new DatagramSocket(12345);
while (true) {
// 創(chuàng)建一個(gè)包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收數(shù)據(jù)
ds.receive(dp);
// 解析數(shù)據(jù)
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println("from " + ip + " data is : " + s);
}
// 釋放資源
// 接收端應(yīng)該一直開著等待接收數(shù)據(jù)签餐,是不需要關(guān)閉
// ds.close();
}
}
/*
* 數(shù)據(jù)來自于鍵盤錄入
* 鍵盤錄入數(shù)據(jù)要自己控制錄入結(jié)束寓涨。
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建發(fā)送端的Socket對象
DatagramSocket ds = new DatagramSocket();
// 封裝鍵盤錄入數(shù)據(jù)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine()) != null) {
if ("886".equals(line)) {
break;
}
// 創(chuàng)建數(shù)據(jù)并打包
byte[] bys = line.getBytes();
// DatagramPacket dp = new DatagramPacket(bys, bys.length,
// InetAddress.getByName("192.168.12.92"), 12345);
DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.12.255"), 12345);
// 發(fā)送數(shù)據(jù)
ds.send(dp);
}
// 釋放資源
ds.close();
}
}
- 把剛才發(fā)送和接收程序分別用線程進(jìn)行封裝,完成一個(gè)UDP的聊天程序氯檐。
/*
* 通過多線程改進(jìn)剛才的聊天程序戒良,這樣我就可以實(shí)現(xiàn)在一個(gè)窗口發(fā)送和接收數(shù)據(jù)了
*/
public class ChatRoom {
public static void main(String[] args) throws IOException {
DatagramSocket dsSend = new DatagramSocket();
DatagramSocket dsReceive = new DatagramSocket(12306);
SendThread st = new SendThread(dsSend);
ReceiveThread rt = new ReceiveThread(dsReceive);
Thread t1 = new Thread(st);
Thread t2 = new Thread(rt);
t1.start();
t2.start();
}
}
public class ReceiveThread implements Runnable {
private DatagramSocket ds;
public ReceiveThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
while (true) {
// 創(chuàng)建一個(gè)包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收數(shù)據(jù)
ds.receive(dp);
// 解析數(shù)據(jù)
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println("from " + ip + " data is : " + s);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class SendThread implements Runnable {
private DatagramSocket ds;
public SendThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
// 封裝鍵盤錄入數(shù)據(jù)
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
String line = null;
while ((line = br.readLine()) != null) {
if ("886".equals(line)) {
break;
}
// 創(chuàng)建數(shù)據(jù)并打包
byte[] bys = line.getBytes();
// DatagramPacket dp = new DatagramPacket(bys, bys.length,
// InetAddress.getByName("192.168.12.92"), 12345);
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("192.168.12.255"), 12306);
// 發(fā)送數(shù)據(jù)
ds.send(dp);
}
// 釋放資源
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP傳輸
- Socket和ServerSocket
- 建立客戶端和服務(wù)器端
- 建立連接后,通過Socket中的IO流進(jìn)行數(shù)據(jù)的傳輸
- 關(guān)閉socket
- 同樣冠摄,客戶端與服務(wù)器端是兩個(gè)獨(dú)立的應(yīng)用程序糯崎。
TCP傳輸-客戶端思路
1:建立客戶端的Socket服務(wù),并明確要連接的服務(wù)器。
2:如果連接建立成功,就表明已經(jīng)建立了數(shù)據(jù)傳輸?shù)耐ǖ篮佑尽>涂梢栽谠撏ǖ劳ㄟ^IO進(jìn)行數(shù)據(jù)的讀取和寫入沃呢。該通道稱為
Socket流
,Socket流中既有讀取流,也有寫入流。3:通過Socket對象的方法,可以獲取這兩個(gè)流拆挥。
4:通過流的對象可以對數(shù)據(jù)進(jìn)行傳輸
5:如果傳輸數(shù)據(jù)完畢,關(guān)閉資源
TCP傳輸-客戶端代碼
/*
* 連接被拒絕薄霜。TCP協(xié)議一定要先開服務(wù)器。
* java.net.ConnectException: Connection refused: connect
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建發(fā)送端的Socket對象
// Socket(InetAddress address, int port)
// Socket(String host, int port)
// Socket s = new Socket(InetAddress.getByName("192.168.12.92"), 8888);
Socket s = new Socket("192.168.12.92", 8888);
// 獲取輸出流竿刁,寫數(shù)據(jù)
// public OutputStream getOutputStream()
OutputStream os = s.getOutputStream();
os.write("hello,tcp,我來了".getBytes());
// 釋放資源
s.close();
}
}
TCP傳輸-服務(wù)器端思路
- 1:建立服務(wù)器端的
socket服務(wù)
黄锤,需要一個(gè)端口
- 2:服務(wù)端沒有直接流的操作,而是通過
accept方法
獲取客戶端對象,再通過獲取到的客戶端對象的流和客戶端進(jìn)行通信食拜。 - 3:通過客戶端的獲取流對象的方法,讀取數(shù)據(jù)或者寫入數(shù)據(jù)
- 4:如果服務(wù)完成,需要關(guān)閉客戶端,然后關(guān)閉服務(wù)器,但是,一般會(huì)關(guān)閉客戶端,不會(huì)關(guān)閉服務(wù)器,因?yàn)榉?wù)端是一直提供服務(wù)的副编。
- TCP傳輸-服務(wù)器端代碼
/*
* TCP協(xié)議接收數(shù)據(jù):
* A:創(chuàng)建接收端的Socket對象
* B:監(jiān)聽客戶端連接负甸。返回一個(gè)對應(yīng)的Socket對象
* C:獲取輸入流,讀取數(shù)據(jù)顯示在控制臺(tái)
* D:釋放資源
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建接收端的Socket對象
// ServerSocket(int port)
ServerSocket ss = new ServerSocket(8888);
// 監(jiān)聽客戶端連接痹届。返回一個(gè)對應(yīng)的Socket對象
// public Socket accept()
Socket s = ss.accept(); // 偵聽并接受到此套接字的連接。此方法在連接傳入之前一直阻塞。
// 獲取輸入流郑兴,讀取數(shù)據(jù)顯示在控制臺(tái)
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys); // 阻塞式方法
String str = new String(bys, 0, len);
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + "---" + str);
// 釋放資源
s.close();
// ss.close(); //這個(gè)不應(yīng)該關(guān)閉
}
}
TCP傳輸案例
- 服務(wù)器給客戶端反饋
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建服務(wù)器Socket對象
ServerSocket ss = new ServerSocket(9999);
// 監(jiān)聽客戶端的連接
Socket s = ss.accept(); // 阻塞
// 獲取輸入流
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys); // 阻塞
String server = new String(bys, 0, len);
System.out.println("server:" + server);
// 獲取輸出流
OutputStream os = s.getOutputStream();
os.write("數(shù)據(jù)已經(jīng)收到".getBytes());
// 釋放資源
s.close();
// ss.close();
}
}
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 9999);
// 獲取輸出流
OutputStream os = s.getOutputStream();
os.write("今天天氣很好,適合睡覺".getBytes());
// 獲取輸入流
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);// 阻塞
String client = new String(bys, 0, len);
System.out.println("client:" + client);
// 釋放資源
s.close();
}
}
- 客戶端鍵盤錄入薪前,服務(wù)器輸出到控制臺(tái)
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建服務(wù)器Socket對象
ServerSocket ss = new ServerSocket(22222);
// 監(jiān)聽客戶端連接
Socket s = ss.accept();
// 包裝通道內(nèi)容的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// br.close();
s.close();
// ss.close();
}
}
/*
* 客戶端鍵盤錄入,服務(wù)器輸出到控制臺(tái)
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 22222);
// 鍵盤錄入數(shù)據(jù)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 把通道內(nèi)的流給包裝一下
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) {
// 鍵盤錄入數(shù)據(jù)要自定義結(jié)束標(biāo)記
if ("886".equals(line)) {
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
// 釋放資源
// bw.close();
// br.close();
s.close();
}
}
- 客戶端鍵盤錄入柴淘,服務(wù)器輸出文本文件
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建服務(wù)器Socket對象
ServerSocket ss = new ServerSocket(23456);
// 監(jiān)聽客戶端連接
Socket s = ss.accept();
// 封裝通道內(nèi)的數(shù)據(jù)
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封裝文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
// br.close();
s.close();
// ss.close();
}
}
- 客戶端文本文件迫淹,服務(wù)器輸出到控制臺(tái)
/*
* 客戶端文本文件秘通,服務(wù)器輸出到控制臺(tái)
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建Socket對象
Socket s = new Socket("192.168.12.92", 34567);
// 封裝文本文件
BufferedReader br = new BufferedReader(new FileReader(
"InetAddressDemo.java"));
// 封裝通道內(nèi)的流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
s.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建服務(wù)器Socket對象
ServerSocket ss = new ServerSocket(34567);
// 監(jiān)聽客戶端連接
Socket s = ss.accept();
// 封裝通道內(nèi)的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
s.close();
}
}
TCP傳輸容易出現(xiàn)的問題
- 客戶端連接上服務(wù)端,兩端都在等待敛熬,沒有任何數(shù)據(jù)傳輸肺稀。
- 通過例程分析:
- 因?yàn)閞ead方法或者readLine方法是阻塞式。
- 解決辦法:
- 自定義結(jié)束標(biāo)記
- 使用shutdownInput应民,shutdownOutput方法话原。
- 客戶端文本文件,服務(wù)器輸出文本文件
/*
* 問題:按照我們正常的思路加入反饋信息诲锹,結(jié)果卻沒反應(yīng)繁仁。為什么呢?
* 讀取文本文件是可以以null作為結(jié)束信息的,但是呢归园,通道內(nèi)是不能這樣結(jié)束信息的黄虱。
* 所以,服務(wù)器根本就不知道你結(jié)束了蔓倍。而你還想服務(wù)器給你反饋悬钳。所以,就相互等待了偶翅。
*
* 如何解決呢?
* A:再多寫一條數(shù)據(jù)默勾,告訴服務(wù)器,讀取到這條數(shù)據(jù)說明我就結(jié)束聚谁,你也結(jié)束吧母剥。
* 這樣做可以解決問題,但是不好形导。
* B:Socket對象提供了一種解決方案
* public void shutdownOutput()
*/
public class UploadClient {
public static void main(String[] args) throws IOException {
// 創(chuàng)建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 11111);
// 封裝文本文件
BufferedReader br = new BufferedReader(new FileReader(
"InetAddressDemo.java"));
// 封裝通道內(nèi)流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
//Socket提供了一個(gè)終止环疼,它會(huì)通知服務(wù)器你別等了,我沒有數(shù)據(jù)過來了
s.shutdownOutput();
// 接收反饋
BufferedReader brClient = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String client = brClient.readLine(); // 阻塞
System.out.println(client);
// 釋放資源
br.close();
s.close();
}
}
public class UploadServer {
public static void main(String[] args) throws IOException {
// 創(chuàng)建服務(wù)器端的Socket對象
ServerSocket ss = new ServerSocket(11111);
// 監(jiān)聽客戶端連接
Socket s = ss.accept();// 阻塞
// 封裝通道內(nèi)的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封裝文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
// if("over".equals(line)){
// break;
// }
bw.write(line);
bw.newLine();
bw.flush();
}
// 給出反饋
BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
bwServer.write("文件上傳成功");
bwServer.newLine();
bwServer.flush();
// 釋放資源
bw.close();
s.close();
}
}
- 上傳圖片案例
public class UploadServer {
public static void main(String[] args) throws IOException {
// 創(chuàng)建服務(wù)器Socket對象
ServerSocket ss = new ServerSocket(19191);
// 監(jiān)聽客戶端連接
Socket s = ss.accept();
// 封裝通道內(nèi)流
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
// 封裝圖片文件
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("mn.jpg"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
// 給一個(gè)反饋
OutputStream os = s.getOutputStream();
os.write("圖片上傳成功".getBytes());
bos.close();
s.close();
}
}
public static void main(String[] args) throws IOException {
// 創(chuàng)建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 19191);
// 封裝圖片文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"林青霞.jpg"));
// 封裝通道內(nèi)的流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
s.shutdownOutput();
// 讀取反饋
InputStream is = s.getInputStream();
byte[] bys2 = new byte[1024];
int len2 = is.read(bys2);
String client = new String(bys2, 0, len2);
System.out.println(client);
// 釋放資源
bis.close();
s.close();
}
}
- 服務(wù)器的代碼用線程進(jìn)行封裝朵耕,這樣可以模擬一個(gè)同時(shí)接收多人上傳文件的服務(wù)器炫隶。(用循環(huán)也可以但是效率低,是單線程的程序)
public class UploadClient {
public static void main(String[] args) throws IOException {
// 創(chuàng)建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 11111);
// 封裝文本文件
// BufferedReader br = new BufferedReader(new FileReader(
// "InetAddressDemo.java"));
BufferedReader br = new BufferedReader(new FileReader(
"ReceiveDemo.java"));
// 封裝通道內(nèi)流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// Socket提供了一個(gè)終止阎曹,它會(huì)通知服務(wù)器你別等了伪阶,我沒有數(shù)據(jù)過來了
s.shutdownOutput();
// 接收反饋
BufferedReader brClient = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String client = brClient.readLine(); // 阻塞
System.out.println(client);
// 釋放資源
br.close();
s.close();
}
}
public class UserThread implements Runnable {
private Socket s;
public UserThread(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
// 封裝通道內(nèi)的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封裝文本文件
// BufferedWriter bw = new BufferedWriter(new
// FileWriter("Copy.java"));
// 為了防止名稱沖突
String newName = System.currentTimeMillis() + ".java";
BufferedWriter bw = new BufferedWriter(new FileWriter(newName));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// 給出反饋
BufferedWriter bwServer = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
bwServer.write("文件上傳成功");
bwServer.newLine();
bwServer.flush();
// 釋放資源
bw.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class UploadServer {
public static void main(String[] args) throws IOException {
// 創(chuàng)建服務(wù)器Socket對象
ServerSocket ss = new ServerSocket(11111);
while (true) {
Socket s = ss.accept();
new Thread(new UserThread(s)).start();
}
}
}