網(wǎng)絡(luò)應(yīng)用模式主要有:
- 主機(jī)/終端模式:集中計(jì)算,集中管理;
- 客戶機(jī)/服務(wù)器(Client/Server,簡(jiǎn)稱C/S)模式:分布計(jì)算,分布管理;
- 瀏覽器/服務(wù)器模式:利用Internet跨平臺(tái)。
www(萬維網(wǎng))就是建立在客戶機(jī)/服務(wù)器模式上,以HTML語言和HTTP協(xié)議為基礎(chǔ),能夠提供各種Internet服務(wù)的信息瀏覽系統(tǒng)。網(wǎng)絡(luò)信息放在主機(jī)的不同位置,www服務(wù)器利用超文本鏈路鏈接各項(xiàng)信息。www客戶機(jī)(瀏覽器Brower)負(fù)責(zé)與服務(wù)器建立聯(lián)系,向服務(wù)器發(fā)送請(qǐng)求,處理HTML超媒體,提供圖形用戶界面(GUI),顯示信息等。
在客戶機(jī)/服務(wù)器工作模式中,在Server端,要準(zhǔn)備接受多個(gè)Client端計(jì)算機(jī)的通信。為此,除用IP地址標(biāo)識(shí)Internet上的計(jì)算機(jī)之外,另還引入端口號(hào),用端口號(hào)標(biāo)識(shí)正在Server端后臺(tái)服務(wù)的線程。端口號(hào)與IP地址的組合稱為網(wǎng)絡(luò)套接字(socket)。
Java語言在實(shí)現(xiàn)C/S模式中,套接字分為兩類:
- 在Server端,ServerSocket類支持底層的網(wǎng)絡(luò)通信;
- 在Client端,Socket類支持網(wǎng)絡(luò)的底層通信。
Server機(jī)通過端口(總線I/O地址)提供面向Client機(jī)的服務(wù);Server機(jī)在它的幾個(gè)不同端口分別同時(shí)提供幾種不同的服務(wù)。Client接入Server的某一端口,通過這個(gè)端口提請(qǐng)Server機(jī)為其服務(wù)。規(guī)定:端口號(hào)0~1023供系統(tǒng)專用。例如,HTTP協(xié)議在端口80,telnet協(xié)議在端口23。端口1024~65535供應(yīng)用程序使用。
當(dāng)Client程序和Server程序需要通信時(shí),可以用Socket類建立套接字連接。套接字連接可想象為一個(gè)電話呼叫:最初是Client程序建立呼叫,Server程序監(jiān)聽;呼叫完成后,任何一方都可以隨時(shí)講話。
雙方實(shí)現(xiàn)通信有流式socket和數(shù)據(jù)報(bào)式socket兩種可選方式:
- 流式socket是有連接的通信,即TCP(Transmission Control Protocol):每次通信前建立連接,通信結(jié)束后斷開連接。特點(diǎn)是可以保證傳輸?shù)恼_性、可靠性。
- 數(shù)據(jù)報(bào)式socket是無連接的通信,即UDP(User Datagram Protocol):將欲傳輸?shù)臄?shù)據(jù)分成 小包,直接上網(wǎng)發(fā)送。無需建立連接和拆除連接,速度快,但無可靠保證。
流式socket在Client程序和Server程序間建立通信的通道。每個(gè)socket可以進(jìn)行讀和寫兩種操作。對(duì)于任一端,與對(duì)方的通信會(huì)話過程是:
建立socket連接,獲得輸入/輸出流,讀數(shù)據(jù)/寫數(shù)據(jù),通信完成后關(guān)閉socket(拆除連接)。
利用socket的構(gòu)造方法,可以在客戶端建立到服務(wù)器的套接字對(duì)象:
Socket(String host,int port):host是服務(wù)器的IP地址,port是端口號(hào),這些是預(yù)先約定的。
例如,代碼:
1
2
3
|
try { Socket mySocket = new Socket(“http: //www.aspku.net” ,1860); } catch (IOException e){} |
然后,用getInputStream()方法獲得輸入流,用這個(gè)輸入流讀取服務(wù)器放入“線路”的信息;用getOutputStream()方法獲得輸出流,用這個(gè)輸出流將信息寫入“線路”。
利用ServerSocket的構(gòu)造方法可以在服務(wù)器建立接受客戶套接字的服務(wù)器套接字對(duì)象:
ServerSocket(int port):指定端口號(hào),創(chuàng)建一個(gè)ServerSocket對(duì)象。端口號(hào)port要與客戶呼叫的端口號(hào)相同。為此,用以下形式代碼:
1
2
3
|
try { ServerSocket serverSocket = new ServerSocket( 1860 ); } catch (IOException e){} |
服務(wù)器端程序在指定的端口監(jiān)聽,當(dāng)收到Client程序發(fā)出的服務(wù)請(qǐng)求時(shí),創(chuàng)建一個(gè)套接字對(duì)象與該端口對(duì)應(yīng)的Client程序通信。例如,執(zhí)行上述建立服務(wù)器套接字對(duì)象的代碼,確立了對(duì)象serverSocket后,就可能它使用accept()方法,得到Socket對(duì)象,接收Client程序來自套接字mySocket的信息。如以下代碼所示:
1
2
3
|
try { Socket sc = serverSocket.accept(); //ac是一個(gè)Socket對(duì)象 } catch (IOException e){} |
要撤銷服務(wù),可以關(guān)閉Socket對(duì)象sc:
1
|
sc.close(); |
【例】C/S模式中的Client端應(yīng)用程序。這是一個(gè)Client端的流式Socket通信的簡(jiǎn)單實(shí)例,代碼說明Client端程序的編寫方法。例中,Client程序向服務(wù)器主機(jī)的端口4441提出請(qǐng)求,連接建立后完成對(duì)服務(wù)器的讀寫。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import java.io.*; import java.net.*; public class Client{ public static void main(String args[]){ String s = null ;Socket mySocket; DataInputStream in = null ;DataOutputStream out = null ; try { mySocket = new Socket(“localhost”, 4441 ); in = new DataInputStream(mySocket.getInputStream()); out = new DataOutputStream(mySocket.getOutputStream()); out.writeUTF(“good server!”); while ( true ){ s = in.readUTF(); if (s== null ) break ; else System.out.println(s); } mySocket.close(); } catch (IOException e){ System.out.println(“can't connect”); } } } |
【例】與Client端應(yīng)用程序?qū)?yīng)的Server端應(yīng)用程序。程序在4441端口監(jiān)聽,當(dāng)檢測(cè)到有客戶機(jī)請(qǐng)求時(shí),產(chǎn)生一個(gè)內(nèi)為“客戶,你好,我是服務(wù)器”的字符串輸出到客戶端。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import java.io.*; import java.net.*; public class Server{ public static void main(String args[]){ ServerSocket server = null ; Socket you = null ;String s = null ; DataOutputStream out = null ; DataInputStream in = null ; try { server = new ServerSocket( 4441 ); } catch (IOException e1){ system.out.println(“ERROR:” +e1); } try { you = server.accept(); in = new DataInputStream(you.getInputStream()); out = new DataOutputStream(you. getOutputStream()); while ( true ){ s = in.readUTF(); if (s!= null ) break ; } out.writeUTF(“客戶,你好,我是服務(wù)器”); out.close(); } catch (IOException e){System.out.println(“ERROR:”+e);} } } |
為了充分發(fā)揮計(jì)算機(jī)的平行工作能力,可以把套接字連接工作讓一個(gè)線程完成。當(dāng)客戶端要請(qǐng)求服務(wù)器給予服務(wù),或當(dāng)服務(wù)器端接收到一個(gè)客戶的服務(wù)請(qǐng)求,就啟動(dòng)一個(gè)專門完成信息通信的線程,在該線程中創(chuàng)建輸入輸出流,并完成客戶端與服務(wù)器端的信息交流。
【例】 將套接字連接工作置于線程的客戶端小應(yīng)用程序。界面在有一個(gè)發(fā)送信息按紐、一個(gè)文本框和一個(gè)文本區(qū)。客戶端應(yīng)用程序首先與服務(wù)器建立套接字連接。使用數(shù)據(jù)輸入流in反復(fù)讀取服務(wù)器放入線路里的信息,將收到的信息在文本區(qū)中顯示。婐志取的信息是“結(jié)束”,則關(guān)閉套接字連接,并結(jié)束程序。用戶也可在文本框輸入信息,并按發(fā)送信息按鈕,則客戶端程序利用數(shù)據(jù)輸出流out,將文本框中的內(nèi)容發(fā)送給服務(wù)器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
import java.net.*; import java.io.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.applet.*; public class Aclient extends Applet implements Runnable,ActionListener{ JButton button; JTextField textF; JTextArea textA; Socket socket; Thread thread; DataInputStream in; DataOutputStream out; public void init(){ setBackground( new Color( 120 , 153 , 137 )); setLayout( new BorderLayout()); Button = new JButton(“發(fā)送信息”); textF = new JTextField( 20 ); textA = new JTextArea( 20 , 30 ); setSize( 450 , 350 ); JPanel p = new JPanel(); p.add(textF); p.add(button); add(textA,”Center”); add(p,”South”); button.addActionListener( this ); } public void start(){ try { socket = new Socket( this .getCodeBase().getHost(), 4441 ); in = new DataInputStream(socket.getInputStream()); out = new DataOutputStream(socket.getOutputStream()); } catch (IOException e){} if (thread== null ){ thread = new Thread( this ); thread.setPriority(Thread.MIN_PRIORITY); thread.start(); } } public void run(){ String s = null ; while ( true ){ try { s = in.readUTF(); } catch (IOException e){} if (s.equals(“結(jié)束”)){ try { socket.close(); break ; } catch (IOException e){} } else texA.append(s + “ ”); } } public void actionPerformed(ActionEvent e){ if (e.getSource()==button){ String s = textF.getText(); if (s! = null ){ try { out.writeUTF(s); } catch (IOException e1){} } else { try { out.writeUTF(“請(qǐng)說話”); } catch (IOException e1){} } } } } |
【例】程序以端4441建立與客戶端的套接字連接,服務(wù)器端收到客戶端的申請(qǐng)后,以客戶的套接字建立一個(gè)線程,并啟動(dòng)。如果沒有客戶申請(qǐng),則繼續(xù)監(jiān)聽客戶的申請(qǐng)。線程按客戶的套接字建立輸入數(shù)據(jù)流in和輸數(shù)據(jù)流out。線程利用in讀取客戶放入線路里的信息。如果接受的信息是“結(jié)束”,則服務(wù)器回復(fù)“結(jié)束”后關(guān)閉套接字連接;否則回復(fù):“我是服務(wù)器你對(duì)我說“,以及服務(wù)器接收到的信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
import java.net.*; import java.io.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.applet.*; public class Aclient extends Applet implements Runnable,ActionListener{ JButton button; JTextField textF; JTextArea textA; Socket socket; Thread thread; DataInputStream in; DataOutputStream out; public void init(){ setBackground( new Color( 120 , 153 , 137 )); setLayout( new BorderLayout()); Button = new JButton(“發(fā)送信息”); textF = new JTextField( 20 ); textA = new JTextArea( 20 , 30 ); setSize( 450 , 350 ); JPanel p = new JPanel(); p.add(textF); p.add(button); add(textA,”Center”); add(p,”South”); button.addActionListener( this ); } public void start(){ try { socket = new Socket( this .getCodeBase().getHost(), 4441 ); in = new DataInputStream(socket.getInputStream()); out = new DataOutputStream(socket.getOutputStream()); } catch (IOException e){} if (thread== null ){ thread = new Thread( this ); thread.setPriority(Thread.MIN_PRIORITY); thread.start(); } } public void run(){ String s = null ; while ( true ){ try { s = in.readUTF(); } catch (IOException e){} if (s.equals(“結(jié)束”)){ try { socket.close(); break ; } catch (IOException e){} } else texA.append(s + “ ”); } } public void actionPerformed(ActionEvent e){ if (e.getSource()==button){ String s = textF.getText(); if (s! = null ){ try { out.writeUTF(s); } catch (IOException e1){} } else { try { out.writeUTF(“請(qǐng)說話”); } catch (IOException e1){} } } } } |