Created
March 29, 2015 12:51
-
-
Save sturgle/9dd17ea5d4435d98b81e to your computer and use it in GitHub Desktop.
simple telnet chat server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package chatdemo.server; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.io.UnsupportedEncodingException; | |
import java.net.InetSocketAddress; | |
import java.net.MalformedURLException; | |
import java.net.ServerSocket; | |
import java.net.URL; | |
import java.net.URLConnection; | |
import java.nio.ByteBuffer; | |
import java.nio.CharBuffer; | |
import java.nio.channels.SelectionKey; | |
import java.nio.channels.Selector; | |
import java.nio.channels.ServerSocketChannel; | |
import java.nio.channels.SocketChannel; | |
import java.nio.charset.Charset; | |
import java.nio.charset.CharsetDecoder; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.Random; | |
import java.util.Set; | |
import java.util.concurrent.BlockingQueue; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentLinkedQueue; | |
import java.util.concurrent.LinkedBlockingQueue; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
public class Server { | |
private static String LINE_SEPARATOR = System.getProperty("line.separator"); | |
private static int PORT = 8000; | |
private static final long PAUSE_BETWEEEN_MSGS = 100; // millisecs | |
private static ConcurrentHashMap<SocketChannel, UserInfo> socketMap | |
= new ConcurrentHashMap<>(); | |
private static ConcurrentHashMap<String, WeatherInfo> weatherMap | |
= new ConcurrentHashMap<>(); | |
private static Set nameSet = Collections.synchronizedSet(new HashSet<String>()); | |
private static BlockingQueue<MessageInfo> messageQueue = new LinkedBlockingQueue<>(); | |
public static void main(String args[]) throws Exception { | |
// Create a new selector | |
Selector selector = Selector.open(); | |
// Open a listener on each port, and register each one | |
ServerSocketChannel ssc = ServerSocketChannel.open(); | |
ssc.configureBlocking(false); | |
ServerSocket ss = ssc.socket(); | |
InetSocketAddress address = new InetSocketAddress(PORT); | |
ss.bind(address); | |
//registers ACCEPT | |
ssc.register(selector, SelectionKey.OP_ACCEPT); | |
System.out.println("Going to listen on " + PORT); | |
startSendMessageService(); | |
startCheckTimeoutService(); | |
// loop over all the sockets that are ready for some activity | |
while (selector.select() > 0) { | |
Set keys = selector.selectedKeys(); | |
Iterator i = keys.iterator(); | |
while (i.hasNext()) { | |
SelectionKey key = (SelectionKey) i.next(); | |
if (key.isAcceptable()) { | |
// this means that a new client has hit the port our main | |
// socket is listening on, so we need to accept the connection | |
// and add the new client socket to our select pool for reading | |
// a command later | |
System.out.println("Accepting connection!"); | |
// this will be the ServerSocketChannel we initially registered | |
// with the selector in main() | |
ServerSocketChannel sch = (ServerSocketChannel) key.channel(); | |
SocketChannel ch = sch.accept(); | |
ch.configureBlocking(false); | |
socketMap.put(ch, new UserInfo()); | |
MessageInfo msgInfo = new MessageInfo("请输入用户名", | |
true, | |
ch); | |
messageQueue.add(msgInfo); | |
ch.register(selector, SelectionKey.OP_READ); | |
} else if (key.isReadable()) { | |
// one of our client sockets has received a command and | |
// we're now ready to read it in | |
SocketChannel ch = (SocketChannel) key.channel(); | |
if (socketMap.containsKey(ch)) { | |
UserInfo user = socketMap.get(ch); | |
user.setTimestamp(System.currentTimeMillis()); | |
ByteBuffer buf = ByteBuffer.allocate(200); | |
ch.read(buf); | |
buf.flip(); | |
Charset charset = Charset.forName("UTF-8"); | |
CharsetDecoder decoder = charset.newDecoder(); | |
CharBuffer cbuf = decoder.decode(buf); | |
String msg = cbuf.toString(); | |
if (msg.endsWith("\r\n")) { | |
msg = msg.substring(0, msg.length() - 2); | |
} | |
if (user.getStatus() == 0) { | |
if (nameSet.contains(msg)) { | |
MessageInfo msgInfo = new MessageInfo("此用户名已存在,请重新输入", | |
true, | |
ch); | |
messageQueue.add(msgInfo); | |
} else { | |
user.setStatus(1); | |
user.setName(msg); | |
nameSet.add(user.getName()); | |
MessageInfo msgInfo = new MessageInfo(user.getName() + "已上线", | |
false, | |
ch); | |
messageQueue.add(msgInfo); | |
MessageInfo welcomeMsgInfo = new MessageInfo(user.getName() + "已上线,现在共有" + nameSet.size() + "人", | |
true, | |
ch); | |
messageQueue.add(welcomeMsgInfo); | |
} | |
} else { | |
if (msg.startsWith("/")) { | |
if (msg.equals("/quit")) { | |
ch.close(); | |
socketMap.remove(ch); | |
nameSet.remove(user.getName()); | |
MessageInfo msgInfo = new MessageInfo(user.getName() + "已下线", | |
false, | |
ch); | |
messageQueue.add(msgInfo); | |
} else { | |
getServiceResult(msg, ch); | |
} | |
} else { | |
MessageInfo msgInfo = new MessageInfo(user.getName() + ";" + msg, | |
false, | |
ch); | |
messageQueue.add(msgInfo); | |
} | |
} | |
} | |
} | |
i.remove(); | |
} | |
} | |
} | |
private static void getServiceResult(String msg, SocketChannel ch) { | |
// get service from network | |
if (msg.startsWith("/天气")) { | |
getWeatherResult(msg, ch); | |
} | |
} | |
private static void getWeatherResult(String msg, SocketChannel ch) { | |
String city = msg.substring("/天气".length()).trim(); | |
if (!weatherMap.containsKey(city)) { | |
WeatherInfo winfo = new WeatherInfo(); | |
winfo.setTimestamp(System.currentTimeMillis()); | |
weatherMap.put(city, winfo); | |
} | |
if (System.currentTimeMillis() - weatherMap.get(city).getTimestamp() > 60 * 60 * 1000 * 2) { | |
try { | |
String encodedText = java.net.URLEncoder.encode(city, "utf-8"); | |
String url = "http://php.weather.sina.com.cn/xml.php?city=" + encodedText + "&password=DJOYnieT8234jlsK&day=0"; | |
weatherMap.get(city).setMessage(readWebContent(url)); | |
} catch (UnsupportedEncodingException ex) { | |
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
} | |
MessageInfo msgInfo = new MessageInfo(weatherMap.get(city).getMessage(), | |
false, | |
ch); | |
messageQueue.add(msgInfo); | |
} | |
/** | |
* This method sends messages to clients | |
*/ | |
private static void startSendMessageService() { | |
new Thread("Send-to-Clients") { | |
public void run() { | |
try { | |
while (true) { | |
MessageInfo msg = messageQueue.take(); | |
if (!msg.isDir()) { | |
for (SocketChannel ch : socketMap.keySet()) { | |
if (msg.getCh() == ch) { | |
continue; | |
} | |
try { | |
if (socketMap.containsKey(ch)) { | |
UserInfo user = socketMap.get(ch); | |
ByteBuffer buf = ByteBuffer.wrap((msg.message + LINE_SEPARATOR).getBytes()); | |
ch.write(buf); | |
} | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} else { | |
try { | |
if (socketMap.containsKey(msg.getCh())) { | |
UserInfo user = socketMap.get(msg.getCh()); | |
{ | |
ByteBuffer buf = ByteBuffer.wrap((msg.message + LINE_SEPARATOR).getBytes()); | |
msg.getCh().write(buf); | |
} | |
} | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
Thread.sleep(PAUSE_BETWEEEN_MSGS); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
}.start(); | |
} | |
private static void startCheckTimeoutService() { | |
new Thread("check-timeout") { | |
public void run() { | |
try { | |
while (true) { | |
for (SocketChannel ch : socketMap.keySet()) { | |
try { | |
UserInfo user = socketMap.get(ch); | |
if (System.currentTimeMillis() - user.getTimestamp() > 60 * 1000) { | |
ch.close(); | |
socketMap.remove(ch); | |
nameSet.remove(user.getName()); | |
MessageInfo msgInfo = new MessageInfo(user.getName() + "已下线", | |
false, | |
ch); | |
messageQueue.add(msgInfo); | |
} | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
Thread.sleep(PAUSE_BETWEEEN_MSGS); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
}.start(); | |
} | |
private static String readWebContent(String urlStr) { | |
try { | |
URL url = new URL(urlStr); | |
URLConnection yc = url.openConnection(); | |
BufferedReader in = new BufferedReader( | |
new InputStreamReader( | |
yc.getInputStream())); | |
String inputLine; | |
String result = ""; | |
while ((inputLine = in.readLine()) != null) { | |
result += inputLine; | |
} | |
in.close(); | |
return result; | |
} catch (MalformedURLException ex) { | |
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); | |
} catch (IOException ex) { | |
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); | |
} finally { | |
return ""; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment