日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Socket编程实现简易聊天室

發布時間:2024/1/17 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Socket编程实现简易聊天室 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

1.Socket基礎知識

  Socket(套接字)用于描述IP地址和端口,是通信鏈的句柄,應用程序可以通過Socket向網絡發出請求或者應答網絡請求。

  Socket是支持TCP/IP協議的網絡通信的基本操作單元,是對網絡通信過程中端點的抽象表示,包含了進行網絡通信所必需的5種信息:連接所使用的協議、本地主機的IP地址、本地進程的協議端口、遠地主機的IP地址以及遠地進程的協議端口。

1.1?Socket的傳輸模式

  Socket有兩種主要的操作方式:面向連接的和無連接的。(TCP/UDP)

  面向連接的Socket操作就像一部電話,Socket必須在發送數據之前與目的地的Socket取得連接,一旦連接建立了,Socket就可以使用一個流接口進行打開、讀寫以及關閉操作。并且,所有發送的數據在另一端都會以相同的順序被接收。

  無連接的Socket操作就像一個郵件投遞,每一個數據報都是一個獨立的單元,它包含了這次投遞的所有信息(目的地址和要發送的內容)。在這個模式下的Socket不需要連接目的地Socket,它只是簡單的投出數據報。

  由此可見,無連接的操作是快速高效的,但是數據安全性不佳;面向連接的操作效率較低,但數據的安全性較好。

  本文主要介紹的是面向連接的Socket操作。

1.2?Socket的構造方法

  Java在包java.net中提供了兩個類Socket和ServerSocket,分別用來表示雙向連接的Socket客戶端和服務器端。

  Socket的構造方法如下:

  (1)Socket(InetAddress?address,?int?port);

  (2)Socket(InetAddress?address,?int?port,?boolean?stream);

  (3)Socket(String?host,?int?port);

  (4)Socket(String?host,?int?port,?boolean?stream);

  (5)Socket(SocketImpl?impl);

  (6)Socket(String?host,?int?port,?InetAddress?localAddr,?int?localPort);

  (7)Socket(InetAddress?address,?int?port,?InetAddrss?localAddr,?int?localPort);

  ServerSocket的構造方法如下:

  (1)ServerSocket(int?port);

  (2)ServerSocket(int?port,?int?backlog);

  (3)ServerSocket(int?port,?int?backlog,?InetAddress?bindAddr);

  其中,參數address、host和port分別是雙向連接中另一方的IP地址、主機名和端口號;參數stream表示Socket是流Socket還是數據報Socket;參數localAddr和localPort表示本地主機的IP地址和端口號;SocketImpl是Socket的父類,既可以用來創建ServerSocket,也可以用來創建Socket。

  如下的代碼在服務器端創建了一個ServerSocket:

1   try { 2   ServerSocket serverSocket = new ServerSocket(50000); //創建一個ServerSocket,用于監聽客戶端Socket的連接請求 3    while(true) { 4    Socket socket = serverSocket.accept();     //每當接收到客戶端的Socket請求,服務器端也相應的創建一個Socket 5       //todo開始進行Socket通信 6    } 7   }catch (IOException e) { 8    e.printStackTrace(); 9   }

  其中,50000是我們自己選擇的用來進行Socket通信的端口號,在創建Socket時,如果該端口號已經被別的服務占用,將會拋出異常。

  通過以上的代碼,我們創建了一個ServerSocket在端口50000監聽客戶端的請求。accept()是一個阻塞函數,就是說該方法被調用后就會一直等待客戶端的請求,直到有一個客戶端啟動并請求連接到相同的端口,然后accept()返回一個對應于該客戶端的Socket。

  那么,如何在客戶端創建并啟動一個Socket呢?

1   try { 2    socket = new Socket("192.168.1.101", 50000); //192.168.1.101是服務器的IP地址,50000是端口號 3     //todo開始進行Socket通信 4   } catch (IOException e) { 5    e.printStackTrace(); 6 }

?  至此,客戶端和服務器端都建立了用于通信的Socket,接下來就可以由各自的Socket分別打開各自的輸入流和輸出流進行通信了。

1.3輸入流和輸出流

  Socket提供了方法getInputStream()和getOutPutStream()來獲得對應的輸入流和輸出流,以便對Socket進行讀寫操作,這兩個方法的返回值分別是InputStream和OutPutStream對象。

  為了便于讀寫數據,我們可以在返回的輸入輸出流對象上建立過濾流,如PrintStream、InputStreamReader和OutputStreamWriter等。

1.4關閉Socket

  可以通過調用Socket的close()方法來關閉Socket。在關閉Socket之前,應該先關閉與Socket有關的所有輸入輸出流,然后再關閉Socket。

?

2.簡易聊天室

  下面就來說說如何通過Socket編程實現一個簡易聊天室。客戶端完成后的運行效果如圖1所示。

  圖1?運行效果

  在該客戶端的界面中,使用了一個TextView控件來顯示聊天記錄。為了方便查看,將兩個用戶也放到了一個界面中,實際上應該啟動兩個模擬器,分別作為兩個用戶的客戶端,此處是為了方便操作才這么做的。

2.1服務器端ServerSocket的實現

  在該實例中,我們在MyEclipse中新建了一個Java工程作為服務器端。在該Java工程中,我們應該完成以下的操作。

  (1)指定端口實例化一個ServerSocket,并調用ServerSocket的accept()方法在等待客戶端連接期間造成阻塞。

  (2)每當接收到客戶端的Socket請求時,服務器端也相應的創建一個Socket,并將該Socket存入ArrayList中。與此同時,啟動一個ServerThread線程來為該客戶端Socket服務。

  以上兩步操作,可以通過以下的代碼來實現:

1   /* 2    * Class : MyServer類,用于監聽客戶端Socket連接請求 3    * Author : 博客園-依舊淡然 4    */ 5   public class MyServer { 6    7    //定義ServerSocket的端口號 8    private static final int SOCKET_PORT = 50000; 9    //使用ArrayList存儲所有的Socket 10    public static ArrayList<Socket> socketList = new ArrayList<Socket>(); 11    12    public void initMyServer() { 13    try { 14         //創建一個ServerSocket,用于監聽客戶端Socket的連接請求 15    ServerSocket serverSocket = new ServerSocket(SOCKET_PORT); 16    while(true) { 17    //每當接收到客戶端的Socket請求,服務器端也相應的創建一個Socket 18    Socket socket = serverSocket.accept(); 19    socketList.add(socket); 20    //每連接一個客戶端,啟動一個ServerThread線程為該客戶端服務 21    new Thread(new ServerThread(socket)).start(); 22    } 23    }catch (IOException e) { 24    e.printStackTrace(); 25    } 26    } 27    28    public static void main(String[] args) { 29    MyServer myServer = new MyServer(); 30    myServer.initMyServer(); 31    } 32   }

?  (3)在啟動的ServerThread線程中,我們需要將讀到的客戶端內容(也就是某一個客戶端Socket發送給服務器端的數據),發送給其他的所有客戶端Socket,實現信息的廣播。ServerThread類的具體實現如下:

1   public class ServerThread implements Runnable { 2    3    //定義當前線程所處理的Socket 4    private Socket socket = null; 5    //該線程所處理的Socket對應的輸入流 6    private BufferedReader bufferedReader = null; 7    8    /* 9    * Function : ServerThread的構造方法 10    * Author : 博客園-依舊淡然 11    */ 12    public ServerThread(Socket socket) throws IOException { 13    this.socket = socket; 14    //獲取該socket對應的輸入流 15    bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 16    } 17    18    /* 19    * Function : 實現run()方法,將讀到的客戶端內容進行廣播 20    * Author : 博客園-依舊淡然 21    */ 22    public void run() { 23    try { 24    String content = null; 25    //采用循環不斷地從Socket中讀取客戶端發送過來的數據 26    while((content = bufferedReader.readLine()) != null) { 27    //將讀到的內容向每個Socket發送一次 28    for(Socket socket : MyServer.socketList) { 29    //獲取該socket對應的輸出流 30    PrintStream printStream = new PrintStream(socket.getOutputStream()); 31    //向該輸出流中寫入要廣播的內容 32    printStream.println(packMessage(content)); 33    34    } 35    } 36    } catch(IOException e) { 37    e.printStackTrace(); 38    } 39    } 40    41    /* 42    * Function : 對要廣播的數據進行包裝 43    * Author : 博客園-依舊淡然 44    */ 45    private String packMessage(String content) { 46    String result = null; 47    SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); //設置日期格式 48    if(content.startsWith("USER_ONE")) { 49    String message = content.substring(8); //獲取用戶發送的真實的信息 50    result = "\n" + "往事如風 " + df.format(new Date()) + "\n" + message; 51    } 52    if(content.startsWith("USER_TWO")) { 53    String message = content.substring(8); //獲取用戶發送的真實的信息 54    result = "\n" + "依舊淡然 " + df.format(new Date()) + "\n" + message; 55    } 56    return result; 57    } 58    59   }

?  其中,在packMessage()方法中,我們對要廣播的數據進行了包裝。因為要分辨出服務器接收到的消息是來自哪一個客戶端Socket的,我們對客戶端Socket發送的消息也進行了包裝,方法是在消息的頭部加上"USER_ONE"來代表用戶"往事如風",在消息的頭部加上"USER_TWO"來代表用戶"依舊淡然"。?

  至此,服務器端的ServerSocket便算是創建好了。

2.2客戶端Socket的實現

  接下來,我們便可以在Android工程中,分別為用戶"往事如風"和"依舊淡然"創建一個客戶端Socket,并啟動一個客戶端線程ClientThread來監聽服務器發來的數據。

  這一過程的具體實現如下:

1 /* 2 * Function : 初始化Socket 3 * Author : 博客園-依舊淡然 4 */ 5 private void initSocket() { 6 try { 7 socketUser1 = new Socket(URL_PATH, SOCKET_PORT); //用戶1的客戶端Socket 8 socketUser2 = new Socket(URL_PATH, SOCKET_PORT); //用戶2的客戶端Socket 9 clientThread = new ClientThread(); //客戶端啟動ClientThread線程,讀取來自服務器的數據 10 clientThread.start(); 11 } catch (IOException e) { 12 e.printStackTrace(); 13 } 14 }

?  ClientThread的具體實現和服務器端的ServerThread線程相似,唯一的區別是,在ClientThread線程中接收到服務器端發來的數據后,我們不可以直接在ClientThread線程中進行刷新UI的操作,而是應該將數據封裝到Message中,再調用MyHandler對象的sendMessage()方法將Message發送出去。這一過程的具體實現如下:

1 /* 2 * Function : run()方法,用于讀取來自服務器的數據 3 * Author : 博客園-依舊淡然 4 */ 5   public void run() { 6   try { 7    String content = null; 8    while((content = bufferedReader .readLine()) != null) { 9    Bundle bundle = new Bundle(); 10    bundle.putString(KEY_CONTENT, content); 11    Message msg = new Message(); 12    msg.setData(bundle); //將數據封裝到Message對象中 13    myHandler.sendMessage(msg); 14    } 15    } catch (Exception e) { 16    e.printStackTrace(); 17    } 18   }

?  最后,我們在UI主線程中創建一個內部類MyHandler,讓它繼承Handler類,并實現handleMessage()方法,用來接收Message消息并處理(刷新UI)。MyContent是一個用來保存聊天記錄的類,提供了get和set接口,其中,set接口設置的本條聊天記錄,而get接口獲得的是全部的聊天記錄。具體的實現如下:

1 /* 2 * Class : 內部類MyHandler,用于接收消息并處理 3 * Author : 博客園-依舊淡然 4 */ 5 private class MyHandler extends Handler { 6 public void handleMessage(Message msg) { 7 Bundle bundle = msg.getData(); //獲取Message中發送過來的數據 8 String content = bundle.getString(KEY_CONTENT); 9 MyContent.setContent(content); //保存聊天記錄 10 mTextView.setText(MyContent.getContent()); 11 } 12 }

?  至此,客戶端的Socket也編寫完成了。

轉載于:https://www.cnblogs.com/berylqliu/p/6261505.html

總結

以上是生活随笔為你收集整理的Socket编程实现简易聊天室的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。