網(wǎng)絡編程是什么
網(wǎng)絡編程的本質(zhì)是兩個設備之間的數(shù)據(jù)交換,當然,在計算機網(wǎng)絡中,設備主要指計算機。數(shù)據(jù)傳遞本身沒有多大的難度,不就是把一個設備中的數(shù)據(jù)發(fā)送給兩外一個設備,然后接受另外一個設備反饋的數(shù)據(jù)。
何為Socket
網(wǎng)絡上的兩個程序通過一個雙向的通信連接實現(xiàn)數(shù)據(jù)的交換,這個連接的一端稱為一個socket。建立網(wǎng)絡通信連接至少要一對端口號(socket)。socket本質(zhì)是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網(wǎng)絡開發(fā)所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數(shù)據(jù)的具體形式;Socket是發(fā)動機,提供了網(wǎng)絡通信的能力。
Socket編程
一個是ServerSocket,一個是Socket。服務端和客戶端之間通過Socket建立連接,之后它們就可以進行通信了。首先ServerSocket將在服務端監(jiān)聽某個端口,當發(fā)現(xiàn)客戶端有Socket來試圖連接它時,它會accept該Socket的連接請求,同時在服務端建立一個對應的Socket與之進行通信。這樣就有兩個Socket了,客戶端和服務端各一個。
對于Socket之間的通信其實很簡單,服務端往Socket的輸出流里面寫東西,客戶端就可以通過Socket的輸入流讀取對應的內(nèi)容。Socket與Socket之間是雙向連通的,所以客戶端也可以往對應的Socket輸出流里面寫東西,然后服務端對應的Socket的輸入流就可以讀出對應的內(nèi)容。
下面是一個例子,客戶端讀寫和服務端讀寫
服務端代碼
public class Server {public static void main(String args[]) throws IOException { //定義一個ServerSocket監(jiān)聽在端口8888上 int port = 8888; int i=1; //連接計數(shù) //server嘗試接收其他Socket的連接請求, ServerSocket server = new ServerSocket(port); //server的accept方法是阻塞式的 ,即等待著客戶端的請求 Socket socket = server.accept(); System.out.println("連接"+i++); //跟客戶端建立好連接,我們就可以獲取socket的InputStream,從中讀取客戶端發(fā)過來的信息。 Reader reader = new InputStreamReader(socket.getInputStream()); char chars[] = new char[64]; int len; StringBuilder sb = new StringBuilder(); String temp; int index; while ((len=reader.read(chars)) != -1) { temp = new String(chars, 0, len); if ((index = temp.indexOf("eof")) != -1) { //遇到eof時就結束接收 sb.append(temp.substring(0, index)); break; } sb.append(temp); } System.out.println("from client: " + sb); //讀完后寫數(shù)據(jù) Writer writer = new OutputStreamWriter(socket.getOutputStream()); writer.write("Hello Client:我是服務端輸入數(shù)據(jù)"); //釋放資源 writer.flush(); writer.close(); reader.close(); socket.close(); server.close(); } }
輸出
注意點
服務端從Socket的InputStream中讀取數(shù)據(jù)的操作也是阻塞式的,如果從輸入流中沒有讀取到數(shù)據(jù)程序會一直在那里不動,直到客戶端往Socket的輸出流中寫入了數(shù)據(jù),或關閉了Socket的輸出流。當然,對于客戶端的Socket也是同樣如此。
輸入流中讀取客戶端發(fā)送過來的數(shù)據(jù),接下來我們再往輸出流里面寫入數(shù)據(jù)給客戶端,接下來關閉對應的資源文件。而實際上上述代碼可能并不會按照我們預先設想的方式運行,因為從輸入流中讀取數(shù)據(jù)是一個阻塞式操作,在上述的while循環(huán)中當讀到數(shù)據(jù)的時候就會執(zhí)行循環(huán)體,否則就會阻塞,這樣后面的寫操作就永遠都執(zhí)行不了了,針對這種情況,通常我們都會約定一個結束標記,當客戶端發(fā)送過來的數(shù)據(jù)包含某個結束標記時就說明當前的數(shù)據(jù)已經(jīng)發(fā)送完畢了,這個時候我們就可以進行循環(huán)的跳出了。
在上述代碼中,當服務端讀取到客戶端發(fā)送的結束標記,即“eof”時就會結束數(shù)據(jù)的接收,終止循環(huán),這樣后續(xù)的代碼又可以繼續(xù)進行了。
客戶端代碼
public class Client { public static void main(String args[]) throws Exception { //為了簡單起見,所有的異常都直接往外拋 String host = "192.168.74.1"; //要連接的服務端IP地址 int port = 8888; //要連接的服務端對應的監(jiān)聽端口 //與服務端建立連接 Socket client = new Socket(host, port); //建立連接后就可以往服務端寫數(shù)據(jù)了 Writer writer = new OutputStreamWriter(client.getOutputStream()); writer.write("Hello ,我是客戶端輸入數(shù)據(jù)"); writer.write("eof"); writer.flush();//寫完后要記得flush //讀取來自服務端數(shù)據(jù) Reader reader = new InputStreamReader(client.getInputStream()); char chars[] = new char[64]; int len; StringBuffer sb = new StringBuffer(); String temp; int index; while ((len=reader.read(chars)) != -1) { temp = new String(chars, 0, len); if ((index = temp.indexOf("eof")) != -1) { sb.append(temp.substring(0, index)); break; } sb.append(new String(chars, 0, len)); } System.out.println("from server: " + sb); writer.close(); reader.close(); client.close(); } }
輸出
注意點
過程:先是給服務端發(fā)送了一段數(shù)據(jù),之后讀取服務端返回來的數(shù)據(jù),跟之前的服務端一樣在讀的過程中有可能導致程序一直掛在那里,永遠跳不出while循環(huán),解決方法和服務端一樣,用一個結束標志。
對于客戶端往Socket的輸出流里面寫數(shù)據(jù)傳遞給服務端要注意一點,如果寫操作之后程序不是對應著輸出流的關閉,而是進行其他阻塞式的操作(比如從輸入流里面讀數(shù)據(jù)),記住要flush一下,只有這樣服務端才能收到客戶端發(fā)送的數(shù)據(jù),否則可能會引起兩邊無限的互相等待。在稍后講到客戶端和服務端同時讀和寫的時候會說到這個問題。
http://www.cnblogs.com/favour/p/7107829.html