Skip to content

Instantly share code, notes, and snippets.

@sturgle
Created March 29, 2015 12:51
Show Gist options
  • Save sturgle/9dd17ea5d4435d98b81e to your computer and use it in GitHub Desktop.
Save sturgle/9dd17ea5d4435d98b81e to your computer and use it in GitHub Desktop.
simple telnet chat server
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