Java中的Socket.、线程和HaspMap的使用
程序的功能:实现多人聊天
关键技术点说明:服务器将每个客户端的线程保存在hashmap中,这样就能实现数据正确转发到哪个窗口。客户端这边将连接服务器的线程也保存起来,同时还要保存线程对应的聊天界面,这样通过读取连接服务器的线程就能正确收到消息,通过线程对应的界面就能将消息正确的显示。通过这个实例熟悉一下面向对象编程。
程序关键代码实现:
服务端:
1.服务器建立ServerSocket
public class MyQqServer {
public MyQqServer()
{
try {
ServerSocket ss=new ServerSocket(3456);
while(true)
{
Socket s=ss.accept();
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
User u=(User)ois.readObject();
Message m=new Message();
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
if(u.getPasswd().equals("12"))
{
m.setMessType("1");
oos.writeObject(m);
SerConClientThread scct=new SerConClientThread(s);
ManageClientThread.addClientThread(u.getUserid(), scct);
scct.start();
scct.notifyother(u.getUserid());
}else
{
m.setMessType("2");
oos.writeObject(m);
s.close();
}
}
} catch (Exception e) {
// TODO: handle exception
}finally{
}
}
2.将客户端的线程放进HashMap
public class ManageClientThread {
public static HashMap<String, SerConClientThread> hm=new HashMap<String,SerConClientThread>();
public static void addClientThread(String uid,SerConClientThread ct)
{
hm.put(uid, ct);
}
public static SerConClientThread getClientThread(String uid)
{
return (SerConClientThread)hm.get(uid);
}
public static String getAllOnLineUserid()
{
Iterator<String> it=hm.keySet().iterator();
String res="";//问题所在 “ ” 这个是错的
while(it.hasNext())
{
res+=it.next().toString()+" ";
}
return res;
}
}
3.当有一个客户端连接进来的时候就创建一个线程
public class SerConClientThread extends Thread{
Socket s;
public SerConClientThread(Socket s){
//把服务器和该客户端的连接赋给s
this.s=s;
}
//让该线程去通知其他用户
public void notifyother(String iam)
{
HashMap<String, SerConClientThread> hm=ManageClientThread.hm;
Iterator<String> it=hm.keySet().iterator();
while(it.hasNext())
{
Message m=new Message();
m.setCon(iam);
m.setMessType(MessageType.message_ret_onLineFriend);
String onLineUserId=it.next().toString();
try {
ObjectOutputStream oos=new ObjectOutputStream(ManageClientThread.getClientThread(onLineUserId).s.getOutputStream());
m.setGetter(onLineUserId);
oos.writeObject(m);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void run(){
while(true){
try {
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
Message m = (Message) ois.readObject();
if(m.getMessType().equals(MessageType.message_comm_mes))
{
//取得接收人的线程 实现转发 ( SerConClientThread是连接的线程 在MyQqServer时候被创建)
SerConClientThread sc=ManageClientThread.getClientThread(m.getGetter());
ObjectOutputStream oos=new ObjectOutputStream(sc.s.getOutputStream());
oos.writeObject(m);
}else if(m.getMessType().equals(MessageType.message_get_onLineFriend))
{
//把在服务器的好友返回给该客户端
String res=ManageClientThread.getAllOnLineUserid();
Message m1=new Message();
m1.setMessType(MessageType.message_ret_onLineFriend);
m1.setCon(res);
m1.setGetter(m.getSender());//服务端发给客服端,客户端是getter, sengder是客户端发的
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
oos.writeObject(m1);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
客户端:
1客户端与服务器的线程放在HashMap,保持聊天界面中是将线程对应的界面放进HashMap。
public class ManageClientConServerThread {
private static HashMap<String, ClientConServerThread> hm=new HashMap<String,ClientConServerThread>();
//把创建好的ClientConServerThread放入到hm
public static void addClientConServerThread(String qqId,ClientConServerThread ccst)
{
hm.put(qqId, ccst);
}
//可以通过qqId取得该线程
public static ClientConServerThread getClientConServerThread(String qqId)
{
return (ClientConServerThread)hm.get(qqId);
}
}
2.这是客户端接收服务器消息在线程中的实现代码
public class ClientConServerThread extends Thread{
private Socket s;
public Socket getS() {
return s;
}
public void setS(Socket s) {
this.s = s;
}
public ClientConServerThread(Socket s){
this.s=s;
}
public void run(){
while(true)
{
try {
//不停的读取从服务器端发来的消息
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
Message m = (Message) ois.readObject();
// System.out.println("读取到从服务发来的消息"+ m.getSender() +" 给 "+m.getGetter()+" 内容"+m.getCon());
//把从服务器获得的消息,显示到该显示的聊天界面
if(m.getMessType().equals(MessageType.message_comm_mes))
{
QqChat qqChat=ManageQqChat.getQqChat(m.getGetter()+" "+m.getSender());//这边是接收 ,所以getGetter是login自己
qqChat.showMessage(m);
}else if(m.getMessType().equals(MessageType.message_ret_onLineFriend)){
System.out.println("客户端接收到的信息"+m.getCon());
String con=m.getCon();
String friends[]=con.split(" ");
String getter=m.getGetter();//返回给发送者, 就是要得到好友列表的send
//修改相应的好友列表
QqFriendList qqFriendList=ManageQqFriendList.getQqFriendList(getter);
//更新好友类表
if(qqFriendList!=null){
qqFriendList.updataFriend(m);
}
}
} catch (Exception e) {
e.printStackTrace();
// TODO: handle exception
}
}
}
}
3.客户端连接服务器的实现
public class QqClientConServer {
public Socket s;
public boolean sendLoginInfoToServer(Object o)
{
boolean b=false;
try{
s = new Socket("211.80.180.73", 3456);
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
oos.writeObject(o);
System.out.print(o);
System.out.println("客服端发送成功");
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
Message ms=(Message)ois.readObject();
System.out.println("收到的信息类型"+ms.getMessType());
if(ms.getMessType().equals("1"))
{
//就创建一个该qq号和服务器保持通讯的连接线程
ClientConServerThread ccst=new ClientConServerThread(s);
// ManageClientConServerThread d=new ManageClientConServerThread();
// d.addClientConServerThread(((User)o).getUserid(), ccst);//证明了是STATIC 可以直接用 在编译时已经实例化
ManageClientConServerThread.addClientConServerThread(((User)o).getUserid(), ccst);
ccst.start();
b=true;
}else{
// s.close();
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally{
}
return b;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
未完成:好多
计划:计算器