多線程與socket寫的聊天工具

上週寫了多線程的代碼, 請看前面的博客. 這幾天又寫了點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();
	}
}
相關文章
相關標籤/搜索