JAVA入门到精通-网络编程补充和QQ案例
------------------------------------------------------
补充讲解:
如何在网络间传递对象?
[MyServer.java]服务器源代码
package com.test.server;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import com.common.User;
public class MyServer {
ServerSocket ss;
Socket s;
ObjectInputStream ois;
public static void main(String[] args) {
new MyServer();
}
//构造函数
public MyServer(){
try {
ss=new ServerSocket(3456);
s=ss.accept();
//以对象流的方式读取(假设客户端发送的是User的一个对象)
ois=new ObjectInputStream(s.getInputStream());
User u=(User)ois.readObject();
//输出
System.out.println(u.getName()+"\t"+u.getPasswd());
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(ois!=null) ois.close();
if(s!=null) s.close();
if(ss!=null) ss.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
[User.java]服务端的公共服务
package com.common;
import java.io.Serializable;
public class User implements Serializable{
private String name;
private String passwd;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
[MyClient.java]客户端源代码
package com.test.client;
import java.io.ObjectOutputStream;
import java.net.Socket;
import com.common.User;
public class MyClient {
Socket s;
ObjectOutputStream oos;
public static void main(String[] args) {
new MyClient();
}
//构造函数
public MyClient(){
try {
s=new Socket("127.0.0.1",3456);
//通过ObjectOutputStream给服务器传送对象
oos=new ObjectOutputStream(s.getOutputStream());
User u=new User();
u.setName("张三");
u.setPasswd("123");
oos.writeObject(u);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(oos!=null) oos.close();
if(s!=null) s.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
[User.java]客户端的公共服务
package com.common;
import java.io.Serializable;
//通过io在网络上传送对象需要进行序列化Serializable
public class User implements Serializable{
private String name;
private String passwd;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
特别说明:
在java网络编程需要通过IO流实现对象的传递需要对,服务端和客户端定义相同的传递对对向方法,以达到实现对象的传递。同时需要对方法进行Serializable序列化,否则报错。
------------------------------
QQ案例
说明:该案例主讲内容:java网络编程、多线程、C/S数据交互
附源代码
注:代码分为Server部分与Client部分,由于此案例仅为对NET操作未使用数据库
Server部分包含的包有:
1、com.qq.server.view (服务端界面包)
1.1、QqServerFrame.java (服务端主页面)
2、com.qq.server.model (业务逻辑包)
2.1、MyQqServer.java (服务器验证客户信息)
2.2、QqServerUser.java (用于管理QQ用户信息[此包功能应为对数据库逻辑操作])(空)
2.3、SerConClientThread.java (服务器连接客户端线程)
2.4、ManageClientThrea.java (管理客户端连接线程)
3、com.qq.server.db (数据库操作包)
3.1、SqlHelper.java (对数据库的CURD操作)(空)
4、com.qq.server.tools (服务端工具包)(空)
5、com.qq.common (通用包)
5.1、Message.java (消息类)
5.2、MessageType.java (消息类型接口类)
5.3、User.java (用户信息类)
Client部分包含的包有:
1、com.qq.client.view (客户端界面包)
1.1、QqClientLogin.java (QQ登录界面)
1.2、QqFriendList.java (好友列表界面)
1.3、QqChat.java (聊天界面)
2、com.qq.client.model (客户端业务逻辑包)
2.1、QqClienConServer.java (客户端连接服务端的后台用于与服务端数据交互)
2.2、QqClientUser.java (客户端用户操作逻辑类)
3、com.qq.client.tools (原应为客户端工具包,现为处理与服务端的线程包)
3.1、ClientConServerThread.java (客户端连接服务器线程类)
3.2、ManageClientConServerThread.java (管理C/S线程类)
3.3、ManageQqChat.java (管理聊天界面类)
3.4、ManageQqFriendList.java (管理好友列表类)
4、com.qq.common (通用包)
4.1、Message.java (消息类)
4.2、MessageType.java(消息类型类)
4.3、User.java (用户信息类)
注意:Server与Client中的com.qq.common通用包主要功能是统一保持客户端与服务端的用户及消息一致。
下见源代码:
Server端源代码:
com.qq.server.view
[QqServerFrame.java]
/**
* 服务器端的控制界面
* 功能:
* 1、完成启动、关闭服务器
* 2、管理及监控用户
*/
package com.qq.server.view;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.qq.server.model.MyQqServer;
public class QqServerFrame extends JFrame implements ActionListener{
JPanel jp1;
JButton jb1,jb2;
public static void main(String []args){
new QqServerFrame();
}
//构造函数
public QqServerFrame(){
jp1=new JPanel();
jb1=new JButton("启动服务器");
jb1.addActionListener(this);
jb2=new JButton("关闭服务器");
jb2.addActionListener(this);
jp1.add(jb1);
jp1.add(jb2);
this.add(jp1);
this.setSize(500, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==jb1){
new MyQqServer();
}else if(e.getSource()==jb2){
System.exit(0);
}
}
}
*******************************************************************************
com.qq.server.model
[MyQqServer.java]
/**
* 这是QQ服务器,它在监听等待某个QQ客户端连接服务器
*/
package com.qq.server.model;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import com.qq.common.Message;
import com.qq.common.User;
public class MyQqServer {
BufferedReader br;
public MyQqServer(){
try {
System.out.println("服务器在监听9999端口...");
//在9999端口监听
ServerSocket ss=new ServerSocket(9999);
//等待连接
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("12345")){
//返回一个成功登录的信息包
m.setMesType("1");
oos.writeObject(m);
//这里就单开一个线程,让该线程与该客户端保持通讯
SerConClientThread scct=new SerConClientThread(s);
ManageClientThrea.addClientThrea(u.getUserId(), scct);
//启动与该客户端通讯的线程
scct.start();
//并通知其它在线用户
scct.notifyOther(u.getUserId());
}else{
m.setMesType("2");
oos.writeObject(m);
//关闭连接
s.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
*******************************************************************************
[SerConClientThread.java]
/**
* 功能:是服务器与某个客户端的通讯线程
*/
package com.qq.server.model;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import com.qq.common.Message;
import com.qq.common.MessageType;
public class SerConClientThread extends Thread{
Socket s;
public SerConClientThread(Socket s){
//把服务器与该客户端的连接赋给s
this.s=s;
}
//让该线程去通知其它在线用户
public void notifyOther(String iam){
//得到所有在线的用户的线程
HashMap hm=ManageClientThrea.hm;
Iterator it=hm.keySet().iterator();
while(it.hasNext()){
Message m=new Message();
m.setCon(iam);
m.setMesType(MessageType.message_ret_onLineFriend);
//取出在线用户的id
String onLineUserId=it.next().toString();
try {
ObjectOutputStream oos=new ObjectOutputStream(ManageClientThrea.getClientThrea(onLineUserId).s.getOutputStream());
m.setGetder(onLineUserId);
oos.writeObject(m);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void run(){
while(true){
//这里该线程就可以接收客户端发来的信息
try {
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
Message m=(Message)ois.readObject();
System.out.println(m.getSender()+" 给 "+m.getGetder()+" 说:"+m.getCon());
//对从客户端取得的消息进行类型判断,然后做相应的处理
if(m.getMesType().equals(MessageType.message_comm_mes)){
//服务器完成转发功能
//取得接收人的通讯线程
SerConClientThread sc=ManageClientThrea.getClientThrea(m.getGetder());
ObjectOutputStream oos=new ObjectOutputStream(sc.s.getOutputStream());
oos.writeObject(m);
}else if(m.getMesType().equals(MessageType.message_get_onLineFriend)){
//把在服务器的好友给该客户端返回
String res=ManageClientThrea.getAllOnLineUserid();
Message m2=new Message();
m2.setMesType(MessageType.message_ret_onLineFriend);
m2.setCon(res);
m2.setGetder(m.getSender());
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
oos.writeObject(m2);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
*******************************************************************************
[ManageClientThrea.java]
/**
* 这是一个管理客户端线程的类
*/
package com.qq.server.model;
import java.util.HashMap;
import java.util.Iterator;
public class ManageClientThrea {
public static HashMap hm=new HashMap<String,SerConClientThread>();
//向hashmap中添加一个客户端通讯线程
public static void addClientThrea(String uid,SerConClientThread scct){
hm.put(uid,scct);
}
public static SerConClientThread getClientThrea(String uid){
return (SerConClientThread)hm.get(uid);
}
//返回当前在线的人的好友情况
public static String getAllOnLineUserid(){
//使用迭代器完成
Iterator it=hm.keySet().iterator();
String res="";
while(it.hasNext()){
res+=it.next().toString()+" ";
}
return res;
}
}
*******************************************************************************
com.qq.common
[Message.java]
/**
* 为Message规定规则
* mesType1-->表明登录成功
* mesType2-->表明登录失败
* mesType3-->表明普通消息包
*/
package com.qq.common;
import java.io.Serializable;
public class Message implements Serializable{
private String mesType;
private String sender;
private String getder;
private String con;
private String sendTime;
public String getGetder() {
return getder;
}
public void setGetder(String getder) {
this.getder = getder;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public String getCon() {
return con;
}
public void setCon(String con) {
this.con = con;
}
public String getSendTime() {
return sendTime;
}
public void setSendTime(String sendTime) {
this.sendTime = sendTime;
}
public String getMesType() {
return mesType;
}
public void setMesType(String mesType) {
this.mesType = mesType;
}
}
*******************************************************************************
[MessageType.java]
/**
* 定义消息包的种类的接口
*/
package com.qq.common;
public interface MessageType {
String message_succeed="1";//登录成功包
String message_login_fail="2";//登录失败包
String message_comm_mes="3";//普通消息包
String message_get_onLineFriend="4";//要求在线的好友包
String message_ret_onLineFriend="5";//返回在线好友包
}
*******************************************************************************
[User.java]
/**
* 这是用户信息类
*/
package com.qq.common;
import java.io.Serializable;
public class User implements Serializable{
private String userId;
private String passwd;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}