本例开发实现了一个多用户的P2P在线聊天程序,C/S结构,客户端可发送消息,然后其他的用户接收到该消息并将其显示在界面中,服务器对信息进行有关处理并向适当的用户发送,同时在服务器端还将显示所有的在线用户,网络管理员即服务器端管理员可选中某用户并将其踢出该聊天室。以上功能的完成主要分为以下几个步骤。
开发环境Eclipse3.2 —— Java开发的IDE工具,用于编写服务端和客户端的功能实现类。
一 Server端
1 监听新用户加入(SimpleServer.java)
新建-〉工程(名为Chat),新建包(名为Server),新建File,名为SimpleServer.java。
在该类中,首先创建一Frame对象,而后再Frame类中添加两个按钮button1(“关闭”)和button2(“踢出”),一个列表框list1以及一个组合框groupBox1,并利用paneLayout进行适当的布局。完成main函数用来显示主窗体对象:
public static void main(String[] args)
{
try
{
SimpleServer simpleServer=new SimpleServer(4321);
simpleServer.setLocation(100,50);
simpleServer.setSize(400,300);
simpleServer.show();
}
catch(Exception e)
{
System.out.print("GotError");
e.printStackTrace() ;
}
}
在主程序中创建一个无限循环,用来监听指定端口查看是否有新的用户加入到聊天室,每监听到一个新的用户则初始化建立一个Socket连接,并作为一个单独的线程保持与该用户的所有联系。
首先定义个ServerSocket,ServerSocket就是服务器端的Socket,它可以监听客户端的连接,而同普通Socket不同就是它的实例化不需要指定主机名而只需要执行特定的端口(若不指定系统就会自动赋予)就可以了:
try
{
protected ServerSocket listenSock;
int listenPort=4321;
listenSock=new ServerSocket(listenPort);
}
catch(Exception e)
{
System.out.println(e.toString());
}
在服务器端已经初始化了一个4321端口用来监听客户端所发送过来的信息,但必须确定服务器该端口是否可用,如果该端口已经被占用则将抛出一个异常而导致该端口初始化失败。初始化该端口之后,则可以加入一个无限循环用来监听消息:
public void waitForClients()
{
while(true)
{
try
{
Socket newClient=listenSock.accept() ;
newConn=new ServerConn(this,newClient);
users.addElement(newConn);
}
catch(Exception bad)
{
bad.printStackTrace() ;
}
}
}
当服务器监听到一个新的客户加入时,便同客户端建立一个Socket连接,同时实例化一个用来控制该用户的实例ServerConn,并将该用户实例加入一个矢量表中,该矢量表存放所有的用户实例对象。
在该实例中网络管理员可以在服务器端对聊天中的在线成员进行简单的维护,也就是可以强制将该用户踢出聊天室。在主窗体中的button2的实现方法如下所示:
void button2_actionPerformed(ActionEvent e)
{
String str=list1.getSelectedItem();
StringTokenizer st=new StringTokenizer(str,"---");
String userIP=st.nextToken();
String userName=st.nextToken();
for(int i=0;i<=users.size();i++)
{
ServerConn conn=(ServerConn)users.elementAt(i);
String ip1=conn.reader.userIP;
String userName1=conn.reader.userName;
if(userIP.equals(ip1)&&userName.equals(userName1))
{
list1.remove(str);
conn.sendString("quit");
conn.reader.stop();
users.removeElement(conn);
}
}
}
通过循环确定当前网络管理员在列表中所选择的用户实例,然后向该用户发送“quit”指令,该指令在客户端将解释执行并强制将该用户从聊天室中踢出;同时服务器端该用户线程停止,用户列表中删除该用户并将该用户实例从矢量表中删除。
最后得到主窗体如程序清单SimpleServer.java所示。
程序清单:
package Server;
import java.awt.*;
import java.util.*;
import java.net.*;
import java.io.*;
import java.awt.event.*;
import com.borland.jbcl.layout.*;
import com.borland.jbcl.control.*;
import java.lang.String;
/**
* <p>Title: </p>
*
* <p>Description: </p>
*
* <p>Copyright: Copyright (c) 2010</p>
*
* <p>Company: </p>
*
* @author www.jianfei5u.com
* @version 1.0
*/
public class SimpleServer extends Frame {
protected ServerConn newConn;
protected ServerSocket listenSock;
GroupBox groupBox1=new GroupBox();
Button button1=new Button() ;
Button button2=new Button() ;
PaneLayout paneLayout1=new PaneLayout();
java.awt.List list1=new java.awt.List();
PaneLayout paneLayout2=new PaneLayout();
Vector users=new Vector();
public SimpleServer(int listenPort) throws Exception {
listenSock=new ServerSocket(listenPort);
try {
jbInit();
} catch (Exception exception) {
exception.printStackTrace();
}
}
public void waitForClients()
{
while(true)
{
try
{
Socket newClient=listenSock.accept() ;
newConn=new ServerConn(this,newClient);
users.addElement(newConn);
}
catch(Exception bad)
{
bad.printStackTrace() ;
}
}
}
public synchronized String processString(String instr)
{
return instr;
}
public static void main(String[] args)
{
try
{
SimpleServer simpleServer=new SimpleServer(4321);
simpleServer.setLocation(100,50);
simpleServer.setSize(400,300);
simpleServer.show();
simpleServer.waitForClients() ;
}
catch(Exception e)
{
System.out.print("GotError");
e.printStackTrace() ;
}
}
public SimpleServer()
{
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace() ;
}
}
private void jbInit() throws Exception {
this.setLayout(paneLayout1);
this.setSize(new Dimension(503,354));
this.setTitle("服务器端");
groupBox1.setLayout(paneLayout2);
groupBox1.setLabel("在线用户");
button1.setLabel("关闭");
button1.addActionListener(new SimpleServer_button1_actionAdapter(this));
button2.setLabel("踢出");
button2.addActionListener(new SimpleServer_button2_actionAdapter(this));
groupBox1.add(list1,new PaneConstraints("list1","list11",
PaneConstraints.ROOT,1.0f));
this.add(groupBox1,new PaneConstraints("groupBox1","groupBox1",
PaneConstraints.ROOT,0.5f));
this.add(button2,new PaneConstraints("button2","groupBox1",
PaneConstraints.BOTTOM,0.1469534f));
this.add(button1,new PaneConstraints("button1","button2",
PaneConstraints.RIGHT,0.5303644f));
}
void tellEveryone(String str)
{
for(int i=0;i<=users.size();i++)
{
ServerConn conn=(ServerConn)users.elementAt(i);
conn.sendString(str);
}
}
void removeServerConn(String ip,String userName)
{
for(int i=0;i<=users.size();i++)
{
ServerConn conn=(ServerConn)users.elementAt(i);
String ip1=conn.reader.userIP;
String userName1=conn.reader.userName;
if(ip.equals(ip1)&&userName.equals(userName1))
users.removeElement(conn);
}
}
void button2_actionPerformed(ActionEvent e)
{
String str=list1.getSelectedItem();
StringTokenizer st=new StringTokenizer(str,"---");
String userIP=st.nextToken();
String userName=st.nextToken();
for(int i=0;i<=users.size();i++)
{
ServerConn conn=(ServerConn)users.elementAt(i);
String ip1=conn.reader.userIP;
String userName1=conn.reader.userName;
if(userIP.equals(ip1)&&userName.equals(userName1))
{
list1.remove(str);
conn.sendString("quit");
conn.reader.stop();
users.removeElement(conn);
}
}
}
void button1_actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
class SimpleServer_button2_actionAdapter implements java.awt.event.ActionListener
{
SimpleServer adaptee;
SimpleServer_button2_actionAdapter(SimpleServer adaptee)
{
this.adaptee =adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button2_actionPerformed(e);
}
}
class SimpleServer_button1_actionAdapter implements java.awt.event.ActionListener
{
SimpleServer adaptee;
SimpleServer_button1_actionAdapter(SimpleServer adaptee)
{
this.adaptee =adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button1_actionPerformed(e);
}
}
未完待续。。。
下一集 编写用户实例类 地址 http://java161.iteye.com/blog/616123