循序渐进Java Socket网络编程(多客户端、信息共享、文件传输)
目錄[-]
- 一、TCP/IP協(xié)議
- 二、TCP與UDP
- 三、Socket是什么
- 四、Java中的Socket
- 五、基本的Client/Server程序
- 六、多客戶端連接服務(wù)器
- 七、信息共享
- 八、文件傳輸
- 推薦閱讀:
前言:在最近一個(gè)即將結(jié)束的項(xiàng)目中使用到了Socket編程,用于調(diào)用另一系統(tǒng)進(jìn)行處理并返回?cái)?shù)據(jù)。故把Socket的基礎(chǔ)知識總結(jié)梳理一遍。
一、TCP/IP協(xié)議
既然是網(wǎng)絡(luò)編程,涉及幾個(gè)系統(tǒng)之間的交互,那么首先要考慮的是如何準(zhǔn)確的定位到網(wǎng)絡(luò)上的一臺(tái)或幾臺(tái)主機(jī),另一個(gè)是如何進(jìn)行可靠高效的數(shù)據(jù)傳輸。這里就要使用到TCP/IP協(xié)議。
TCP/IP協(xié)議(傳輸控制協(xié)議)由網(wǎng)絡(luò)層的IP協(xié)議和傳輸層的TCP協(xié)議組成。IP層負(fù)責(zé)網(wǎng)絡(luò)主機(jī)的定位,數(shù)據(jù)傳輸?shù)穆酚?#xff0c;由IP地址可以唯一的確定Internet上的一臺(tái)主機(jī)。TCP層負(fù)責(zé)面向應(yīng)用的可靠的或非可靠的數(shù)據(jù)傳輸機(jī)制,這是網(wǎng)絡(luò)編程的主要對象。
二、TCP與UDP
TCP是一種面向連接的保證可靠傳輸?shù)膮f(xié)議。通過TCP協(xié)議傳輸,得到的是一個(gè)順序的無差錯(cuò)的數(shù)據(jù)流。發(fā)送方和接收方的成對的兩個(gè)socket之間必須建立連接,以便在TCP協(xié)議的基礎(chǔ)上進(jìn)行通信,當(dāng)一個(gè)socket(通常都是server socket)等待建立連接時(shí),另一個(gè)socket可以要求進(jìn)行連接,一旦這兩個(gè)socket連接起來,它們就可以進(jìn)行雙向數(shù)據(jù)傳輸,雙方都可以進(jìn)行發(fā)送或接收操作。
UDP是一種面向無連接的協(xié)議,每個(gè)數(shù)據(jù)報(bào)都是一個(gè)獨(dú)立的信息,包括完整的源地址或目的地址,它在網(wǎng)絡(luò)上以任何可能的路徑傳往目的地,因此能否到達(dá)目的地,到達(dá)目的地的時(shí)間以及內(nèi)容的正確性都是不能被保證的。
TCP與UDP區(qū)別:
TCP特點(diǎn):
1、TCP是面向連接的協(xié)議,通過三次握手建立連接,通訊完成時(shí)要拆除連接,由于TCP是面向連接協(xié)議,所以只能用于點(diǎn)對點(diǎn)的通訊。而且建立連接也需要消耗時(shí)間和開銷。
2、TCP傳輸數(shù)據(jù)無大小限制,進(jìn)行大數(shù)據(jù)傳輸。
3、TCP是一個(gè)可靠的協(xié)議,它能保證接收方能夠完整正確地接收到發(fā)送方發(fā)送的全部數(shù)據(jù)。
UDP特點(diǎn):
1、UDP是面向無連接的通訊協(xié)議,UDP數(shù)據(jù)包括目的端口號和源端口號信息,由于通訊不需要連接,所以可以實(shí)現(xiàn)廣播發(fā)送。
2、UDP傳輸數(shù)據(jù)時(shí)有大小限制,每個(gè)被傳輸?shù)臄?shù)據(jù)報(bào)必須限定在64KB之內(nèi)。
3、UDP是一個(gè)不可靠的協(xié)議,發(fā)送方所發(fā)送的數(shù)據(jù)報(bào)并不一定以相同的次序到達(dá)接收方。
TCP與UDP應(yīng)用:
1、TCP在網(wǎng)絡(luò)通信上有極強(qiáng)的生命力,例如遠(yuǎn)程連接(Telnet)和文件傳輸(FTP)都需要不定長度的數(shù)據(jù)被可靠地傳輸。但是可靠的傳輸是要付出代價(jià)的,對數(shù)據(jù)內(nèi)容正確性的檢驗(yàn)必然占用計(jì)算機(jī)的處理時(shí)間和網(wǎng)絡(luò)的帶寬,因此TCP傳輸?shù)男什蝗鏤DP高。
2,UDP操作簡單,而且僅需要較少的監(jiān)護(hù),因此通常用于局域網(wǎng)高可靠性的分散系統(tǒng)中client/server應(yīng)用程序。例如視頻會(huì)議系統(tǒng),并不要求音頻視頻數(shù)據(jù)絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會(huì)更合理一些。
三、Socket是什么
Socket通常也稱作"套接字",用于描述IP地址和端口,是一個(gè)通信鏈的句柄。網(wǎng)絡(luò)上的兩個(gè)程序通過一個(gè)雙向的通訊連接實(shí)現(xiàn)數(shù)據(jù)的交換,這個(gè)雙向鏈路的一端稱為一個(gè)Socket,一個(gè)Socket由一個(gè)IP地址和一個(gè)端口號唯一確定。應(yīng)用程序通常通過"套接字"向網(wǎng)絡(luò)發(fā)出請求或者應(yīng)答網(wǎng)絡(luò)請求。?Socket是TCP/IP協(xié)議的一個(gè)十分流行的編程界面,但是,Socket所支持的協(xié)議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯(lián)系的。在Java環(huán)境下,Socket編程主要是指基于TCP/IP協(xié)議的網(wǎng)絡(luò)編程。
Socket通訊過程:服務(wù)端監(jiān)聽某個(gè)端口是否有連接請求,客戶端向服務(wù)端發(fā)送連接請求,服務(wù)端收到連接請求向客戶端發(fā)出接收消息,這樣一個(gè)連接就建立起來了。客戶端和服務(wù)端都可以相互發(fā)送消息與對方進(jìn)行通訊。
Socket的基本工作過程包含以下四個(gè)步驟:
1、創(chuàng)建Socket;
2、打開連接到Socket的輸入輸出流;
3、按照一定的協(xié)議對Socket進(jìn)行讀寫操作;
4、關(guān)閉Socket。
四、Java中的Socket
在java.net包下有兩個(gè)類:Socket和ServerSocket。ServerSocket用于服務(wù)器端,Socket是建立網(wǎng)絡(luò)連接時(shí)使用的。在連接成功時(shí),應(yīng)用程序兩端都會(huì)產(chǎn)生一個(gè)Socket實(shí)例,操作這個(gè)實(shí)例,完成所需的會(huì)話。對于一個(gè)網(wǎng)絡(luò)連接來說,套接字是平等的,并沒有差別,不因?yàn)樵诜?wù)器端或在客戶端而產(chǎn)生不同級別。不管是Socket還是ServerSocket它們的工作都是通過SocketImpl類及其子類完成的。
列出幾個(gè)常用的構(gòu)造方法:
?| 1 2 3 4 5 6 7 8 9 | Socket(InetAddress address,int?port);//創(chuàng)建一個(gè)流套接字并將其連接到指定 IP 地址的指定端口號 Socket(String host,int?port);//創(chuàng)建一個(gè)流套接字并將其連接到指定主機(jī)上的指定端口號 Socket(InetAddress address,int?port, InetAddress localAddr,int?localPort);//創(chuàng)建一個(gè)套接字并將其連接到指定遠(yuǎn)程地址上的指定遠(yuǎn)程端口 Socket(String host,int?port, InetAddress localAddr,int?localPort);//創(chuàng)建一個(gè)套接字并將其連接到指定遠(yuǎn)程主機(jī)上的指定遠(yuǎn)程端口 Socket(SocketImpl impl);//使用用戶指定的 SocketImpl 創(chuàng)建一個(gè)未連接 Socket ServerSocket(int?port);//創(chuàng)建綁定到特定端口的服務(wù)器套接字 ServerSocket(int?port,int?backlog);//利用指定的 backlog 創(chuàng)建服務(wù)器套接字并將其綁定到指定的本地端口號 ServerSocket(int?port,int?backlog, InetAddress bindAddr);//使用指定的端口、偵聽 backlog 和要綁定到的本地 IP地址創(chuàng)建服務(wù)器 |
構(gòu)造方法的參數(shù)中,address、host和port分別是雙向連接中另一方的IP地址、主機(jī)名和端?口號,stream指明socket是流socket還是數(shù)據(jù)報(bào)socket,localPort表示本地主機(jī)的端口號,localAddr和bindAddr是本地機(jī)器的地址(ServerSocket的主機(jī)地址),impl是socket的父類,既可以用來創(chuàng)建serverSocket又可以用來創(chuàng)建Socket。count則表示服務(wù)端所能支持的最大連接數(shù)。
注意:必須小心選擇端口號。每一個(gè)端口提供一種特定的服務(wù),只有給出正確的端口,才?能獲得相應(yīng)的服務(wù)。0~1023的端口號為系統(tǒng)所保留,例如http服務(wù)的端口號為80,telnet服務(wù)的端口號為21,ftp服務(wù)的端口號為23,?所以我們在選擇端口號時(shí),最好選擇一個(gè)大于1023的數(shù)以防止發(fā)生沖突。
幾個(gè)重要的Socke方法:
?| 1 2 3 | public?InputStream getInputStream();//方法獲得網(wǎng)絡(luò)連接輸入,同時(shí)返回一個(gè)IutputStream對象實(shí)例 public?OutputStream getOutputStream();//方法連接的另一端將得到輸入,同時(shí)返回一個(gè)OutputStream對象實(shí)例 public?Socket accept();//用于產(chǎn)生"阻塞",直到接受到一個(gè)連接,并且返回一個(gè)客戶端的Socket對象實(shí)例。 |
"阻塞"是一個(gè)術(shù)語,它使程序運(yùn)行暫時(shí)"停留"在這個(gè)地方,直到一個(gè)會(huì)話產(chǎn)生,然后程序繼續(xù);通常"阻塞"是由循環(huán)產(chǎn)生的。
注意:其中g(shù)etInputStream和getOutputStream方法均會(huì)產(chǎn)生一個(gè)IOException,它必須被捕獲,因?yàn)樗鼈兎祷氐牧鲗ο?#xff0c;通常都會(huì)被另一個(gè)流對象使用。
五、基本的Client/Server程序
以下是一個(gè)基本的客戶端/服務(wù)器端程序代碼。主要實(shí)現(xiàn)了服務(wù)器端一直監(jiān)聽某個(gè)端口,等待客戶端連接請求。客戶端根據(jù)IP地址和端口號連接服務(wù)器端,從鍵盤上輸入一行信息,發(fā)送到服務(wù)器端,然后接收服務(wù)器端返回的信息,最后結(jié)束會(huì)話。這個(gè)程序一次只能接受一個(gè)客戶連接。
ps:這個(gè)小例子寫好后,服務(wù)端一直接收不到消息,調(diào)試了好長時(shí)間,才發(fā)現(xiàn)誤使用了PrintWriter的print()方法,而BufferedReader的readLine()方法一直沒有遇到換行,所以一直等待讀取。我暈死~~@_@
客戶端程序:
| 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 | package sock; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; ?? public class SocketClient { ????public static void main(String[] args) { ????????try { ????????????/** 創(chuàng)建Socket*/ ????????????// 創(chuàng)建一個(gè)流套接字并將其連接到指定 IP 地址的指定端口號(本處是本機(jī)) ????????????Socket socket =new Socket("127.0.0.1",2013); ????????????// 60s超時(shí) ????????????socket.setSoTimeout(60000); ?? ????????????/** 發(fā)送客戶端準(zhǔn)備傳輸?shù)男畔?*/ ????????????// 由Socket對象得到輸出流,并構(gòu)造PrintWriter對象 ????????????PrintWriter printWriter =new PrintWriter(socket.getOutputStream(),true); ????????????// 將輸入讀入的字符串輸出到Server ????????????BufferedReader sysBuff =new BufferedReader(new InputStreamReader(System.in)); ????????????printWriter.println(sysBuff.readLine()); ????????????// 刷新輸出流,使Server馬上收到該字符串 ????????????printWriter.flush(); ?? ????????????/** 用于獲取服務(wù)端傳輸來的信息 */ ????????????// 由Socket對象得到輸入流,并構(gòu)造相應(yīng)的BufferedReader對象 ????????????BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream())); ????????????// 輸入讀入一字符串 ????????????String result = bufferedReader.readLine(); ????????????System.out.println("Server say : " + result); ?? ????????????/** 關(guān)閉Socket*/ ????????????printWriter.close(); ????????????bufferedReader.close(); ????????????socket.close(); ????????}catch (Exception e) { ????????????System.out.println("Exception:" + e); ????????} ????} } |
服務(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 | package sock; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; ?? public class SocketServer { ????public static void main(String[] args) { ????????try { ????????????/** 創(chuàng)建ServerSocket*/ ????????????// 創(chuàng)建一個(gè)ServerSocket在端口2013監(jiān)聽客戶請求 ????????????ServerSocket serverSocket =new ServerSocket(2013); ????????????while (true) { ????????????????// 偵聽并接受到此Socket的連接,請求到來則產(chǎn)生一個(gè)Socket對象,并繼續(xù)執(zhí)行 ????????????????Socket socket = serverSocket.accept(); ?? ????????????????/** 獲取客戶端傳來的信息 */ ????????????????// 由Socket對象得到輸入流,并構(gòu)造相應(yīng)的BufferedReader對象 ????????????????BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream())); ????????????????// 獲取從客戶端讀入的字符串 ????????????????String result = bufferedReader.readLine(); ????????????????System.out.println("Client say : " + result); ?? ????????????????/** 發(fā)送服務(wù)端準(zhǔn)備傳輸?shù)?*/ ????????????????// 由Socket對象得到輸出流,并構(gòu)造PrintWriter對象 ????????????????PrintWriter printWriter =new PrintWriter(socket.getOutputStream()); ????????????????printWriter.print("hello Client, I am Server!"); ????????????????printWriter.flush(); ?? ????????????????/** 關(guān)閉Socket*/ ????????????????printWriter.close(); ????????????????bufferedReader.close(); ????????????????socket.close(); ????????????} ????????}catch (Exception e) { ????????????System.out.println("Exception:" + e); ????????}finally{ //????????? serverSocket.close(); ????????} ????} } |
六、多客戶端連接服務(wù)器
上面的服務(wù)器端程序一次只能連接一個(gè)客戶端,這在實(shí)際應(yīng)用中顯然是不可能的。通常的網(wǎng)絡(luò)環(huán)境是多個(gè)客戶端連接到某個(gè)主機(jī)進(jìn)行通訊,所以我們要對上面的程序進(jìn)行改造。
設(shè)計(jì)思路:服務(wù)器端主程序監(jiān)聽某一個(gè)端口,客戶端發(fā)起連接請求,服務(wù)器端主程序接收請求,同時(shí)構(gòu)造一個(gè)線程類,用于接管會(huì)話。當(dāng)一個(gè)Socket會(huì)話產(chǎn)生后,這個(gè)會(huì)話就會(huì)交給線程進(jìn)行處理,主程序繼續(xù)進(jìn)行監(jiān)聽。
下面的實(shí)現(xiàn)程序流程是:客戶端和服務(wù)器建立連接,客戶端發(fā)送消息,服務(wù)端根據(jù)消息進(jìn)行處理并返回消息,若客戶端申請關(guān)閉,則服務(wù)器關(guān)閉此連接,雙方通訊結(jié)束。
客戶端程序:
| 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 | package sock; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; ?? public class SocketClient { ????public static void main(String[] args) { ????????try { ????????????Socket socket =new Socket("127.0.0.1",2013); ????????????socket.setSoTimeout(60000); ?? ????????????PrintWriter printWriter =new PrintWriter(socket.getOutputStream(),true); ????????????BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream())); ?????????????? ????????????String result =""; ????????????while(result.indexOf("bye") == -1){ ????????????????BufferedReader sysBuff =new BufferedReader(new InputStreamReader(System.in)); ????????????????printWriter.println(sysBuff.readLine()); ????????????????printWriter.flush(); ?????????????????? ????????????????result = bufferedReader.readLine(); ????????????????System.out.println("Server say : " + result); ????????????} ?? ????????????printWriter.close(); ????????????bufferedReader.close(); ????????????socket.close(); ????????}catch (Exception e) { ????????????System.out.println("Exception:" + e); ????????} ????} } |
服務(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 | package sock; import java.io.*; import java.net.*; ?? public class Server extends ServerSocket { ????private static final int SERVER_PORT =2013; ?? ????public Server()throws IOException { ????????super(SERVER_PORT); ?? ????????try { ????????????while (true) { ????????????????Socket socket = accept(); ????????????????new CreateServerThread(socket);//當(dāng)有請求時(shí),啟一個(gè)線程處理 ????????????} ????????}catch (IOException e) { ????????}finally { ????????????close(); ????????} ????} ?? ????//線程類 ????class CreateServerThread extends Thread { ????????private Socket client; ????????private BufferedReader bufferedReader; ????????private PrintWriter printWriter; ?? ????????public CreateServerThread(Socket s)throws IOException { ????????????client = s; ?? ????????????bufferedReader =new BufferedReader(new InputStreamReader(client.getInputStream())); ?????????????? ????????????printWriter =new PrintWriter(client.getOutputStream(),true); ????????????System.out.println("Client(" + getName() +") come in..."); ?????????????? ????????????start(); ????????} ?? ????????public void run() { ????????????try { ????????????????String line = bufferedReader.readLine(); ?? ????????????????while (!line.equals("bye")) { ????????????????????printWriter.println("continue, Client(" + getName() +")!"); ????????????????????line = bufferedReader.readLine(); ????????????????????System.out.println("Client(" + getName() +") say: " + line); ????????????????} ????????????????printWriter.println("bye, Client(" + getName() +")!"); ?????????????????? ????????????????System.out.println("Client(" + getName() +") exit!"); ????????????????printWriter.close(); ????????????????bufferedReader.close(); ????????????????client.close(); ????????????}catch (IOException e) { ????????????} ????????} ????} ?? ????public static void main(String[] args)throws IOException { ????????new Server(); ????} } |
七、信息共享
以上雖然實(shí)現(xiàn)了多個(gè)客戶端和服務(wù)器連接,但是仍然是消息在一個(gè)客戶端和服務(wù)器之間相互傳播。現(xiàn)在我們要實(shí)現(xiàn)信息共享,即服務(wù)器可以向多個(gè)客戶端發(fā)送廣播消息,客戶端也可以向其他客戶端發(fā)送消息。類似于聊天室的那種功能,實(shí)現(xiàn)信息能在多個(gè)客戶端之間共享。
設(shè)計(jì)思路:客戶端循環(huán)可以不停輸入向服務(wù)器發(fā)送消息,并且啟一個(gè)線程,專門用來監(jiān)聽服務(wù)器端發(fā)來的消息并打印輸出。服務(wù)器端啟動(dòng)時(shí),啟動(dòng)一個(gè)監(jiān)聽何時(shí)需要向客戶端發(fā)送消息的線程。每次接受客戶端連接請求,都啟一個(gè)線程進(jìn)行處理,并且將客戶端信息存放到公共集合中。當(dāng)客戶端發(fā)送消息時(shí),服務(wù)器端將消息順序存入隊(duì)列中,當(dāng)需要輸出時(shí),從隊(duì)列中取出廣播到各客戶端處。客戶端輸入showuser命令可以查看在線用戶列表,輸入bye向服務(wù)器端申請退出連接。
PS:以下代碼在測試時(shí)發(fā)現(xiàn)了一個(gè)中文亂碼小問題,當(dāng)文件設(shè)置UTF-8編碼時(shí),無論怎樣在代碼中設(shè)置輸入流編碼都不起作用,輸入中文仍然會(huì)亂碼。把文件設(shè)置為GBK編碼后,不用在代碼中設(shè)置輸入流編碼都能正常顯示傳輸中文。
客戶端代碼:
| 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 65 66 67 68 69 70 71 72 73 | package sock; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; ?? public class SocketClient extends Socket{ ?? ????private static final String SERVER_IP ="127.0.0.1"; ????private static final int SERVER_PORT =2013; ?????? ????private Socket client; ????private PrintWriter out; ????private BufferedReader in; ?????? ????/** ?????* 與服務(wù)器連接,并輸入發(fā)送消息 ?????*/ ????public SocketClient()throws Exception{ ????????super(SERVER_IP, SERVER_PORT); ????????client =this; ????????out =new PrintWriter(this.getOutputStream(),true); ????????in =new BufferedReader(new InputStreamReader(this.getInputStream())); ????????new readLineThread(); ?????????? ????????while(true){ ????????????in =new BufferedReader(new InputStreamReader(System.in)); ????????????String input = in.readLine(); ????????????out.println(input); ????????} ????} ?????? ????/** ?????* 用于監(jiān)聽服務(wù)器端向客戶端發(fā)送消息線程類 ?????*/ ????class readLineThread extends Thread{ ?????????? ????????private BufferedReader buff; ????????public readLineThread(){ ????????????try { ????????????????buff =new BufferedReader(new InputStreamReader(client.getInputStream())); ????????????????start(); ????????????}catch (Exception e) { ????????????} ????????} ?????????? ????????@Override ????????public void run() { ????????????try { ????????????????while(true){ ????????????????????String result = buff.readLine(); ????????????????????if("byeClient".equals(result)){//客戶端申請退出,服務(wù)端返回確認(rèn)退出 ????????????????????????break; ????????????????????}else{//輸出服務(wù)端發(fā)送消息 ????????????????????????System.out.println(result); ????????????????????} ????????????????} ????????????????in.close(); ????????????????out.close(); ????????????????client.close(); ????????????}catch (Exception e) { ????????????} ????????} ????} ?????? ????public static void main(String[] args) { ????????try { ????????????new SocketClient();//啟動(dòng)客戶端 ????????}catch (Exception e) { ????????} ????} } |
服務(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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | package sock; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; ?? ?? public class Server extends ServerSocket{ ?????? ????private static final int SERVER_PORT =2013; ?????? ????private static boolean isPrint =false;//是否輸出消息標(biāo)志 ????private static List user_list =new ArrayList();//登錄用戶集合 ????private static List<ServerThread> thread_list =new ArrayList<ServerThread>();//服務(wù)器已啟用線程集合 ????private static LinkedList<String> message_list =new LinkedList<String>();//存放消息隊(duì)列 ?????? ????/** ?????* 創(chuàng)建服務(wù)端Socket,創(chuàng)建向客戶端發(fā)送消息線程,監(jiān)聽客戶端請求并處理 ?????*/ ????public Server()throws IOException{ ????????super(SERVER_PORT);//創(chuàng)建ServerSocket ????????new PrintOutThread();//創(chuàng)建向客戶端發(fā)送消息線程 ?????????? ????????try { ????????????while(true){//監(jiān)聽客戶端請求,啟個(gè)線程處理 ????????????????Socket socket = accept(); ????????????????new ServerThread(socket); ????????????} ????????}catch (Exception e) { ????????}finally{ ????????????close(); ????????} ????} ?????? ????/** ?????* 監(jiān)聽是否有輸出消息請求線程類,向客戶端發(fā)送消息 ?????*/ ????class PrintOutThread extends Thread{ ?????????? ????????public PrintOutThread(){ ????????????start(); ????????} ?????????? ????????@Override ????????public void run() { ????????????while(true){ ????????????????if(isPrint){//將緩存在隊(duì)列中的消息按順序發(fā)送到各客戶端,并從隊(duì)列中清除。 ????????????????????String message = message_list.getFirst(); ????????????????????for (ServerThread thread : thread_list) { ????????????????????????thread.sendMessage(message); ????????????????????} ????????????????????message_list.removeFirst(); ????????????????????isPrint = message_list.size() >0 ?true :false; ????????????????} ????????????} ????????} ????} ?????? ????/** ?????* 服務(wù)器線程類 ?????*/ ????class ServerThread extends Thread{ ????????private Socket client; ????????private PrintWriter out; ????????private BufferedReader in; ????????private String name; ?????????? ????????public ServerThread(Socket s)throws IOException{ ????????????client = s; ????????????out =new PrintWriter(client.getOutputStream(),true); ????????????in =new BufferedReader(new InputStreamReader(client.getInputStream())); ????????????in.readLine(); ????????????out.println("成功連上聊天室,請輸入你的名字:"); ????????????start(); ????????} ?????????? ????????@Override ????????public void run() { ????????????try { ????????????????int flag =0; ????????????????String line = in.readLine(); ????????????????while(!"bye".equals(line)){ ????????????????????//查看在線用戶列表 ????????????????????if ("showuser".equals(line)) { ????????????????????????out.println(this.listOnlineUsers()); ????????????????????????line = in.readLine(); ????????????????????} ????????????????????//第一次進(jìn)入,保存名字 ????????????????????if(flag++ ==0){ ????????????????????????name = line; ????????????????????????user_list.add(name); ????????????????????????thread_list.add(this); ????????????????????????out.println(name +"你好,可以開始聊天了..."); ????????????????????????this.pushMessage("Client<" + name +">進(jìn)入聊天室..."); ????????????????????}else{ ????????????????????????this.pushMessage("Client<" + name +"> say : " + line); ????????????????????} ????????????????????line = in.readLine(); ????????????????} ????????????????out.println("byeClient"); ????????????}catch (Exception e) { ????????????????e.printStackTrace(); ????????????}finally{//用戶退出聊天室 ????????????????try { ????????????????????client.close(); ????????????????}catch (IOException e) { ????????????????????e.printStackTrace(); ????????????????} ????????????????thread_list.remove(this); ????????????????user_list.remove(name); ????????????????pushMessage("Client<" + name +">退出了聊天室"); ????????????} ????????} ?????????? ????????//放入消息隊(duì)列末尾,準(zhǔn)備發(fā)送給客戶端 ????????private void pushMessage(String msg){ ????????????message_list.addLast(msg); ????????????isPrint =true; ????????} ?????????? ????????//向客戶端發(fā)送一條消息 ????????private void sendMessage(String msg){ ????????????out.println(msg); ????????} ?????????? ????????//統(tǒng)計(jì)在線用戶列表 ????????private String listOnlineUsers() { ????????????String s ="--- 在線用戶列表 ---\015\012"; ????????????for (int i =0; i < user_list.size(); i++) { ????????????????s +="[" + user_list.get(i) +"]\015\012"; ????????????} ????????????s +="--------------------"; ????????????return s; ????????} ????} ?????? ????public static void main(String[] args)throws IOException { ????????new Server();//啟動(dòng)服務(wù)端 ????} } |
八、文件傳輸
客戶端向服務(wù)器端傳送文件,服務(wù)端可獲取文件名用于保存,獲取文件大小計(jì)算傳輸進(jìn)度,比較簡單,直接貼代碼。
客戶端代碼:
| 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 | package sock; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.net.Socket; ?? /** ?* 客戶端 ?*/ public class Client extends Socket{ ?????? ????private static final String SERVER_IP ="127.0.0.1"; ????private static final int SERVER_PORT =2013; ?????? ????private Socket client; ????private FileInputStream fis; ????private DataOutputStream dos; ?????? ????public Client(){ ????????try { ????????????try { ????????????????client =new Socket(SERVER_IP, SERVER_PORT); ????????????????//向服務(wù)端傳送文件 ????????????????File file =new File("c:/test.doc"); ????????????????fis =new FileInputStream(file); ????????????????dos =new DataOutputStream(client.getOutputStream()); ?????????????????? ????????????????//文件名和長度 ????????????????dos.writeUTF(file.getName()); ????????????????dos.flush(); ????????????????dos.writeLong(file.length()); ????????????????dos.flush(); ?????????????????? ????????????????//傳輸文件 ????????????????byte[] sendBytes =new byte[1024]; ????????????????int length =0; ????????????????while((length = fis.read(sendBytes,0, sendBytes.length)) >0){ ????????????????????dos.write(sendBytes,0, length); ????????????????????dos.flush(); ????????????????} ????????????}catch (Exception e) { ????????????????e.printStackTrace(); ????????????}finally{ ????????????????if(fis !=null) ????????????????????fis.close(); ????????????????if(dos !=null) ????????????????????dos.close(); ????????????????client.close(); ????????????} ????????}catch (Exception e) { ????????????e.printStackTrace(); ????????} ????} ?????? ????public static void main(String[] args)throws Exception { ????????new Client(); ????} } |
服務(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 65 66 67 | package sock; import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; import java.net.ServerSocket; import java.net.Socket; ?? /** ?* 服務(wù)器 ?*/ public class Server extends ServerSocket{ ?? ????private static final int PORT =2013; ?????? ????private ServerSocket server; ????private Socket client; ????private DataInputStream dis; ????private FileOutputStream fos; ?????? ????public Server()throws Exception{ ????????try { ????????????try { ????????????????server =new ServerSocket(PORT); ?????????????????? ????????????????while(true){ ????????????????????client = server.accept(); ?????????????????????? ????????????????????dis =new DataInputStream(client.getInputStream()); ????????????????????//文件名和長度 ????????????????????String fileName = dis.readUTF(); ????????????????????long fileLength = dis.readLong(); ????????????????????fos =new FileOutputStream(new File("d:/" + fileName)); ?????????????????????? ????????????????????byte[] sendBytes =new byte[1024]; ????????????????????int transLen =0; ????????????????????System.out.println("----開始接收文件<" + fileName +">,文件大小為<" + fileLength +">----"); ????????????????????while(true){ ????????????????????????int read =0; ????????????????????????read = dis.read(sendBytes); ????????????????????????if(read == -1) ????????????????????????????break; ????????????????????????transLen += read; ????????????????????????System.out.println("接收文件進(jìn)度" +100 * transLen/fileLength +"%..."); ????????????????????????fos.write(sendBytes,0, read); ????????????????????????fos.flush(); ????????????????????} ????????????????????System.out.println("----接收文件<" + fileName +">成功-------"); ????????????????????client.close(); ????????????????} ????????????}catch (Exception e) { ????????????????e.printStackTrace(); ????????????}finally { ????????????????if(dis !=null) ????????????????????dis.close(); ????????????????if(fos !=null) ????????????????????fos.close(); ????????????????server.close(); ????????????} ????????}catch (Exception e) { ????????????e.printStackTrace(); ????????} ????} ?????? ????public static void main(String[] args)throws Exception { ????????new Server(); ????} } |
轉(zhuǎn)載于:https://www.cnblogs.com/huangwentian/p/7810532.html
總結(jié)
以上是生活随笔為你收集整理的循序渐进Java Socket网络编程(多客户端、信息共享、文件传输)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 希尔排序增量研究
- 下一篇: Java基础——JVM内存结构