上週寫了多線程的代碼, 請看前面的博客. 這幾天又寫了點socket的代碼. 主要目的是爲了後面的一篇文章<tomcat原理>. 但在寫下面文章以前, 我決定先要介紹下socket的用法. java
這篇文章將使用socket和多線程來實現一個相似QQ的功能. 代碼下載之後能夠直接eclipse中運行. 固然這裏的socket使用的是TCP, 而在實際應用中, 聊天工具通常都是用UDP的, java也提供了UDP的socket. tomcat
socket的實現起來,須要兩個類來支持, ServerSocket和Socket. 通俗的講, ServerSocket建立一個監聽socket的server, socket則是client與server之間的消息通道. 服務器
啓動一個監聽server, 以下. 意思爲啓動一個server,在端口5555上監聽. accept()阻塞.一旦有socket連接進來,accept()方法會建立一個新socket通道. 多線程
ServerSocket server = new ServerSocket(5555); Socket socket = server.accept();以上是server端. client端使用如下方法與server進行連接.
Socket socket = new Socket("localhost", 5555);
通道創建之後,經過socket.getInputStream()和socket.getOutputStream()獲得的流來傳輸內容. app
注意: 對於socket, inputstream, outputstream中的任意一個執行close方法,都會致使socket close. eclipse
這篇文章主要實現了一個簡單的聊天工具. 它即沒有容錯性, 也沒有考慮性能. 只是明白一個聊天工具的設計原理. 你們隨便跑跑吧. 工具須要3個類來支撐, Server.java, Client.java, MessageHelper.java. Server就是聊天工具的server端實現. Client是用戶端實現. MessageHelper是幫助解析和封裝傳輸消息的工具. socket
Server端啓動了一個監聽線程, 每監聽到一個socket進來,先將socket註冊到Map中,而後啓動一個新的線程來處理socket通訊.
Client端建立消息通道之後,向server發送本身的name, 而後啓動2個線程Receiver和Sender來分別處理接受和發送消息. ide
本例的實現方式也只能存在與咱們這個簡單的例子中,由於它的性能很是的差. 只有server端監聽,每監聽到一個socket就啓動一個線程來通訊,在用戶註銷前,沒法斷開鏈接,也沒法銷燬線程,那一億的用戶就須要一億個線程. 這將會耗盡服務器資源.將來的改進方法爲, client也啓動一個ServerSocket. client向server鏈接,建立socket,當信息傳輸之後,能夠關閉socket. 當Server須要向client傳輸消息的時候,它須要鏈接client的ServerSocket,當傳輸完畢,斷開鏈接. 這樣子,Server就不須要一直維護一個server-client的鏈接, 雙發只須要知道對方的監聽地址和端口,只在須要向對方傳遞消息的時候,才建立鏈接. 工具
1, 將三個類下載或複製到eclipse的項目中.
2, 在Server.java中運行 main方法,啓動server.
3, 在Client.java中運行 main方法,啓動client. 能夠啓動任意多個client.
4, client啓動之後, 須要用戶使用一個名字來login. 好比, joey, bill, marry, jack...
5, 發送消息的格式爲: To bill: Hello bill.
6, 全部的傳輸消息都會在server端打印log. 性能
你們所看到的以下
Bill client端發起聊天
Joey client端收到消息,回覆消息
Server端的log爲
最後,貼上全部的代碼.
Server.java
package test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; public class Server { private static Map<String, Socket> sockets = new HashMap<String, Socket>(); /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(5555); while (true) { Socket socket = server.accept(); try { InputStream input = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(input, "utf-8")); String nickName =br.readLine(); sockets.put(nickName.trim().toLowerCase(), socket); System.out.println(nickName + " connects to server."); } catch (IOException e) { e.printStackTrace(); } new MessageSwitchService(socket).start(); } } public static class MessageSwitchService extends Thread { private Socket socket; public MessageSwitchService(Socket socket) { this.socket = socket; } @Override public void run() { try { InputStream input = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(input, "utf-8")); String line; while ((line=br.readLine())!=null) { System.out.println(line); MessageHelper.Message msg = MessageHelper.parseRawMessage(line); Socket target = sockets.get(msg.getTo().toLowerCase()); PrintWriter printer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(target.getOutputStream(), "utf-8")), true); printer.println(line); } } catch (IOException e) { e.printStackTrace(); } System.out.println("close"); } } }Client.java
package test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.UnknownHostException; public class Client { private Socket socket; private String nickName; public Client() throws IOException { BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(System.in, "utf-8")); System.out.print("Login as:"); this.nickName = br.readLine(); this.socket = new Socket("localhost", 5555); PrintWriter printer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8")), true); printer.println(nickName); //printer.close(); new Thread(new Receiver()).start(); new Thread(new Sender()).start(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public class Receiver implements Runnable { @Override public void run() { BufferedReader br=null; try { br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8")); String line; while ((line=br.readLine())!=null) { MessageHelper.Message msg = MessageHelper.parseRawMessage(line); System.out.print(msg.getFrom() + " says:"); System.out.println(msg.getBody()); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (br!=null) try { br.close(); } catch (IOException e) { e.printStackTrace(); } if (!socket.isClosed()) try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } public class Sender implements Runnable { @Override public void run() { PrintWriter printer = null; BufferedReader reader = null; try { printer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8")), true); reader = new BufferedReader(new InputStreamReader(System.in, "utf-8")); String line; while ((line = reader.readLine())!=null) { line = "From " + nickName + " " + line; line = MessageHelper.produceRawMessage(MessageHelper.parseRawMessage(line)); printer.println(line); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (printer!=null) printer.close(); if (reader!=null) try { reader.close(); } catch (IOException e) { e.printStackTrace(); } if (!socket.isClosed()) try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @param args * @throws IOException * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException, IOException { new Client(); } }MessageHelper.java
package test; public class MessageHelper { public static final String WHITESPACE=" "; public static class Message { private final String from; private final String to; private final String body; public Message(String from, String to, String body) { this.from = from; this.to = to; this.body = body; } public String getFrom() { return this.from; } public String getTo() { return this.to; } public String getBody() { return this.body; } } // Raw message format: From user1 to user2: message body. public static Message parseRawMessage(String raw) { raw = raw.trim(); while(raw.indexOf(WHITESPACE + WHITESPACE)>0) { raw = raw.replaceAll(WHITESPACE+WHITESPACE, WHITESPACE); } String[] arr = raw.split(":"); String header = arr[0]; String body = arr[1].trim(); arr = header.split(WHITESPACE); String from = arr[1]; String to = arr[3]; return new Message(from, to, body); } public static String produceRawMessage(Message message) { StringBuffer sb = new StringBuffer(); sb.append("From").append(WHITESPACE).append(message.from).append(WHITESPACE) .append("To").append(WHITESPACE).append(message.to).append(":").append(message.body); return sb.toString(); } }