时间:2018-12-01
概述:聊天室 服务器 客户端
在网络上经常进各种聊天室,本例通过编程实现了C/S 模式的聊天室服务器和客户端。实现方法:ChatServer 类实现了一个聊天室服务器端, 此类有一个构造器, 在构造器中使用newServerSocket()建立一个Socket 连接。RunServer()方法中运行聊天服务,通过Socket 类的getInetAddress().getHostName() 方法获得主机名, 建立新的ChatHandler 对象实例, 在synchronized (clients)中取得与客户端的同步。内部类ChatHandler 继承了Thread 类,在此线程run()方法中处理一次交谈。Close()方法关闭与客户端的连接。ChatRoom 类继承了Applet,在init()中通过new Frame()创建聊天窗体,并在此窗体中设置窗体布局。在login()方法中,通过new Socket(serverHost, port)建立与服务器的连接,通过new Thread()产生新的聊天线程。在logout()方法中,使用sock.close()关闭连接。程序代码:
1.编写Chat 类的基本框架,该类定义了从客户端到服务器的信息的第一个字节常量,该类还包括返回值为布尔型的isValidLoginName()方法,该方法用于判断登录名是否合法,代码如下:
public class Chat
{
public static final int PORTNUM = 9999;
public static final int MAX_LOGIN_LENGTH = 20;
public static final char SEPARATOR = '\\';
public static final char COMMAND = '\\';
public static final char CMD_LOGIN = 'L';
public static final char CMD_QUIT = 'Q';
public static final char CMD_MESG = 'M';
public static final char CMD_BCAST = 'B';
public static final char RESP_PUBLIC = 'P';
public static final char RESP_PRIVATE = 'M';
public static final char RESP_SYSTEM = 'S';
public static boolean isValidLoginName(String login)
{
if (login.length() > MAX_LOGIN_LENGTH)
return false;
return true;
}
}
2.编写ChatServer 类基本框架,该类为聊天室服务器程序。该类包括如下字段:字符串常量CHATMASTER_ID 和SEP,ServerSocket 对象变量servSock,ArrayList 对象变量clients,布尔型变量DEBUG。该类包括main()方法、构造器方法、runServer()方法、log()方法,该类还包括一个内部类ChatHandler,代码如下:
public class ChatServer
{
protected final static String CHATMASTER_ID = "ChatMaster";
protected final static String SEP = ": ";
protected ServerSocket servSock;
protected ArrayList clients;
private static boolean DEBUG = false;
//方法
public static void main(String[] argv);
ChatServer();
public void runServer();
protected void log(String s);
protected class ChatHandler extends Thread;
}
3.Main()方法用于构建一个ChatServer 类实例,运行服务器程序,代码如下:
public static void main(String[] argv)
{
System.out.println("Chat Server 0.1 starting...");
if (argv.length == 1 && argv[0].equals("-debug"))
DEBUG = true;
ChatServer w = new ChatServer();
w.runServer();
System.out.println("**ERROR* Chat Server 0.1 quitting");
}
4.ChatServer 的构造器用于构建该类实例,代码如下:
ChatServer()
{
clients = new ArrayList();
try
{
servSock = new ServerSocket(Chat.PORTNUM);
System.out.println("Chat Server Listening on port " + Chat.PORTNUM);
}
catch(IOException e)
{
log("IO Exception in ChatServer.");
System.exit(0);
}
}
5.RunServer()方法用于运行服务器程序:
public void runServer() {
try
{
while (true)
{
Socket us = servSock.accept();
String hostName = us.getInetAddress().getHostName();
System.out.println("Accepted from " + hostName);
ChatHandler cl = new ChatHandler(us, hostName);
synchronized (clients) {
clients.add(cl);
cl.start();
if (clients.size() == 1)
cl.send(CHATMASTER_ID, "Welcome! you're the first one here");
else {
cl.send(CHATMASTER_ID, "Welcome! you're the latest of " +clients.size() + " users.");
}
}
}
} catch(IOException e) {
log("IO Exception in runServer: " + e);
System.exit(0);
}
}
6.Log()方法用于输出字符串,代码如下:
protected void log(String s)
{
System.out.println(s);
}
7.编写内部类ChatHandler 的基本框架,该类用于处理每个交谈信息,该类继承了Thread 类,包括如下字段:Socket 对象变量clientSock,BufferedReader 对象变量is,PrintWriter 对象变量pw,字符串变量clientIP 和login,该类还包括构造器方法、run()方法、close()方法、send()方法、psend()方法、broadcast()方法、lookup()方法、toString()方法,代码如下:
protected class ChatHandler extends Thread {
protected Socket clientSock;
protected BufferedReader is;
protected PrintWriter pw;
protected String clientIP;
protected String login;
public ChatHandler(Socket sock, String clnt) throws IOException;
public void run();
protected void close();
public void send(String sender, String mesg);
protected void psend(String sender, String msg);
public void broadcast(String sender, String mesg);
protected ChatHandler lookup(String nick);
public String toString();
}
8.ChatHandler 构造器用于构建该类实例,代码如下:
public ChatHandler(Socket sock, String clnt) throws IOException
{
clientSock = sock;
clientIP = clnt;
is = new BufferedReader( new InputStreamReader(sock.getInputStream()));
pw = new PrintWriter(sock.getOutputStream(), true);
}
9.run()方法用于用于处理每一次交谈:
public void run()
{
String line;
try {
while ((line = is.readLine()) != null) {
char c = line.charAt(0);
line = line.substring(1);
switch (c) {
case Chat.CMD_LOGIN:
if (!Chat.isValidLoginName(line)) {
send(CHATMASTER_ID, "LOGIN " + line + " invalid");
log("LOGIN INVALID from " + clientIP);
continue;
}
login = line;
broadcast(CHATMASTER_ID, login + " joins us, for a total of " +clients.size() + " users");
break;
case Chat.CMD_MESG:
if (login == null) {
send(CHATMASTER_ID, "please login first");
continue;
}
int where = line.indexOf(Chat.SEPARATOR);
String recip = line.substring(0, where);
String mesg = line.substring(where+1);
log("MESG: " + login + "-->" + recip + ": "+ mesg);
ChatHandler cl = lookup(recip);
if (cl == null)
psend(CHATMASTER_ID, recip + " not logged in.");
else
cl.psend(login, mesg);
break;
case Chat.CMD_QUIT:
broadcast(CHATMASTER_ID, "Goodbye to " + login + "@" + clientIP);
close();
return;
case Chat.CMD_BCAST:
if (login != null)
broadcast(login, line);
else
log("B
break;
default:
log("Unknown cmd " + c + " from " + login + "@" + clientIP);
}
}
} catch (IOException e) {
log("IO Exception: " + e);
} finally {
System.out.println(login + SEP + "All Done");
synchronized(clients) {
clients.remove(this);
if (clients.size() == 0) {
System.out.println(CHATMASTER_ID + SEP +"Im so lonely I could cry...");
} else if (clients.size() == 1) {
ChatHandler last = (ChatHandler)clients.get(0);
last.send(CHATMASTER_ID,"Hey, you're talking to yourself again");
} else {
broadcast(CHATMASTER_ID,"There are now " + clients.size() + " users");
}
}
}
}
10.close()方法用于关闭服务器:
protected void close() {
if (clientSock == null) {
log("close when not open");
return;
}
try {
clientSock.close();
clientSock = null;
} catch (IOException e) {
log("Failure during close to " + clientIP);
}
}
11.Send()方法用于发送一条信息给用户,代码如下:
public void send(String sender, String mesg)
{
pw.println(sender + SEP + mesg);
}
12.Psend()方法用于发送一条私有信息给用户:
protected void psend(String sender, String msg)
{
send("", msg);
}
13.Broadcast()方法用于发送一条信息给所有用户:
public void broadcast(String sender, String mesg)
{
System.out.println("Broadcasting " + sender + SEP + mesg);
for (int i=0; i
ChatHandler sib = (ChatHandler)clients.get(i);
if (DEBUG)
System.out.println("Sending to " + sib);
sib.send(sender, mesg);
}
if (DEBUG) System.out.println("Done broadcast");
}
14.Lookup()方法用于与客户端同步:
protected ChatHandler lookup(String nick)
{
synchronized(clients) {
for (int i=0; i
ChatHandler cl = (ChatHandler)clients.get(i);
if (cl.login.equals(nick))
return cl;
}
}
return null;
}
15.toString()方法用于将ChatHandler 作为字符串返回:
public String toString()
{
return "ChatHandler[" + login + "]";
}
16.ChatServer 类应该引入如下包:
import java.io.*;
import java.net.*;
import java.util.*;