JAVA 即时网络通信我的服务器
JAVA 即时网络通信我的服务器
以前上Java课的时候,老师要求,自行组队来做一个即时网络通信的课程设计。具体要求:使用Socket套接字和ServerSocket来开发一个基于c/s架构的小项目,服务器和客户端的UI采用Swing编程,具体业务逻辑采用多线程开发。现在过去这么久了,想去回忆一下,记录一下当时的点滴,作为一点点积累。我正在努力回忆..
我主要负责,服务器的设计开发,下面是我的部分代码。
一,UI部分是模仿别人写的,可自行设计。
二,业务部分(多线程处理)
1.线程管理类
package com.haoyudian.server.service;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.haoyudian测试数据mon.util.Constants;
import com.haoyudian测试数据mon.util.DateHelper;
/**
* 服务器管理类 接受用户登录、离线、转发消息
*
* @author Scherrer
*
*/
public class ServerManager {
private ExecutorService executorService; // 线程池
private ServerSocket serverSocket = null ;
private Socket socket;
private Boolean isStart = true ;
public ServerManager() {
try {
// 创建线程池,池中具有(cpu个数*50)条线程
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors() * 50 );
serverSocket = new ServerSocket(Constants.SERVER_PORT);
System.out.println( "服务器IP="
+ Inet4Address.getLocalHost().getHostAddress());
} catch (Exception e) {
e.printStackTrace();
exit();
}
}
/**
* 退出方法
*/
private void exit() {
try {
this .isStart = false ;
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
System.out.println(DateHelper.getDateByCN() + " 服务器已启动..." );
try {
while (isStart) {
socket = serverSocket.accept();
String ip = socket.getRemoteSocketAddress().toString();
System.out.println(DateHelper.getDateByCN()
+ " 用户:" + ip + " 已建立连接" );
// 为支持多用户并发访问,采用线程池管理每一个用户的连接请求
if (socket.isConnected()) {
executorService.execute( new SocketTask(socket)); // 添加到线程池
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null )
socket.close();
if (serverSocket != null )
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private final class SocketTask implements Runnable {
private Socket socket = null ;
private InputThread inThread;
private OutputThread outThread;
private OutputThreadMap threadMap;
public SocketTask(Socket socket) {
this .socket = socket;
threadMap = OutputThreadMap.getInstance();
}
@Override
public void run() {
outThread = new OutputThread(socket); //
// 先实例化写消息线程,(把对应用户的写线程存入map缓存器中)
inThread = new InputThread(socket, outThread, threadMap); // 再实例化读消息线程
outThread.setStart( true );
inThread.setStart( true );
inThread.start();
outThread.start();
}
}
public static void main(String[] args) {
new ServerManager().start();
}
}
2.发送消息的线程
package com.haoyudian.server.service;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import com.haoyudian测试数据mon.bean.trans.TransObject;
/**
* 发送消息的线程
*
* @author Scherrer
*
*/
public class OutputThread extends Thread{
private ObjectOutputStream oos; // 对象输出流
private TransObject<?> object; // 传输对象
private boolean isStart = true ; // 循环标志
private Socket socket; // 套接字
// private OutputThreadMap outMap; // 发送现场缓存对象
public OutputThread(Socket s) {
try {
this .socket = s;
// 构造器里实例化对象输出流
oos = new ObjectOutputStream( this .socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 调用写消息线程,设置了消息之后,唤醒run方法,可以节约资源
* @param object
*/
public void setMessage(TransObject<?> object) {
this .object = object;
synchronized ( this ) {
notify();
}
}
public void setStart( boolean isStart) {
this .isStart = isStart;
}
@Override
public void run() {
try {
while (isStart) {
// 没有消息写的时候,线程等待
synchronized ( this ) {
wait();
}
if (object != null ) {
oos.writeObject(object);
oos.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (oos != null ) {
oos.close();
}
if (socket != null ) {
socket.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
3.接受消息的线程
package com.haoyudian.server.service;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.List;
import com.haoyudian测试数据mon.bean.TextMessage;
import com.haoyudian测试数据mon.bean.User;
import com.haoyudian测试数据mon.bean.trans.TransObject;
import com.haoyudian测试数据mon.bean.trans.TransObjectType;
import com.haoyudian测试数据mon.util.DateHelper;
import com.haoyudian.server.dao.UserDao;
import com.haoyudian.server.dao.UserDaoFactory;
public class InputThread extends Thread{
private ObjectInputStream ois; // 对象读入流
private Socket socket; // socket对象
private OutputThread outThread; // 把接收的消息发送给用户
private OutputThreadMap map; // 写消息的缓存器
private boolean isStart = true ;
public InputThread(Socket socket,OutputThread out,OutputThreadMap map) {
try {
this .socket = socket;
this .outThread = out;
this .map = map;
this .ois = new ObjectInputStream(socket.getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
public void setStart( boolean isStart) { // 提供接口给外部关闭读消息线程
this .isStart = isStart;
}
@Override
public void run() {
try {
while (isStart) {
// 读消息
try {
readMessage();
} catch (Exception e) {
// e.printStackTrace();
break ;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (ois != null ) {
ois.close();
}
if (socket != null ) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void readMessage() throws IOException, ClassNotFoundException {
Object readObj = ois.readObject(); // 读取消息对象
UserDao dao = UserDaoFactory.getInstance(); // 通过dao模式管理后台
if (readObj != null && readObj instanceof TransObject) {
TransObject <?> transObj = (TransObject<?>) readObj; // 转换成传输对象
switch (transObj.getType()) {
case REGISTER: // 注册
User user = (User) transObj.getObject();
int regAccout = dao.register(user);
System.out.println(DateHelper.getDateByCN() +
"新用户注册: " + regAccout);
// 回复用户
TransObject<User> regResult = new
TransObject <User> (TransObjectType.REGISTER);
User u = new User();
u.setAccount(regAccout);
regResult.setObject(u);
System.out.println( "验证一下账号: " + u.getAccount());
outThread.setMessage(regResult);
break ;
case LOGIN:
User loginUser = (User) transObj.getObject();
List <User> list = dao.login(loginUser);
System.out.println( "好友列表: " + list.size());
// 返回 list
TransObject<List<User>> msg = new TransObject<> (TransObjectType.LOGIN);
if (list != null ) { // 登陆成功
TransObject<User> userOnlineMsg = new
TransObject <User> (TransObjectType.LOGIN);
// 此处 new 一个User ,只广播用户账号,如果用loginUser,则可能泄露密码
User tempUser = new User();
tempUser.setAccount(loginUser.getAccount());
// tempUser.setNickname(nickname) // 考虑广播昵称
userOnlineMsg.setObject(tempUser);
for (OutputThread out : map.getAll()) { // 拿到所有用户的发送线程
out.setMessage(userOnlineMsg);
}
// 记录当前用户的发送线程
map.add(loginUser.getAccount(), outThread);
// 设置消息
msg.setObject(list);
}
// 发送
outThread.setMessage(msg);
System.out.println(DateHelper.getDateByCN() + " 用户:"
+ loginUser.getAccount() + " 上线了" );
break ;
case MESSAGE:
// 如果是转发消息(可添加群发)
// 获取消息中要转发的对象id,然后获取缓存的该对象的写线程
int toAccount = transObj.getToUser().getAccount(); // 获取账号
OutputThread ot = map.getById(toAccount); // 获取发送线程
if (ot != null ) {
ot.setMessage(transObj); // 把接收到的消息对象直接发送给另一个用户
} else { // 用户的缓存发送线程为空,表示表示用户已下线,回复用户
TextMessage text = new TextMessage();
text.setTextMessage( "对方离线,您的消息暂时保存在服务器" );
TransObject <TextMessage> msgTip =
new TransObject<> (TransObjectType.MESSAGE);
msgTip.setObject(text);
User tempU = new User();
tempU.setAccount( 0 );
msgTip.setFromUser(tempU);
outThread.setMessage(msgTip);
}
break ;
case LOGOUT: // 下线处理
// 如果是退出,更新数据库在线状态,同时群发告诉所有在线用户
User logoutUser = (User) transObj.getObject();
System.out.println(DateHelper.getDateByCN()
+ "用户: " + logoutUser.getNickname() + "下线了哈" );
dao.logout(logoutUser);
// 结束自己的读消息的线程
isStart = false ;
// 移除自己的缓存线程
map.remove(logoutUser.getAccount());
outThread.setMessage( null ); // 先要设置一个空消息去唤醒写线程
outThread.setStart( false ); // 再结束写线程循环
TransObject <User> offObject = new TransObject<User> (
TransObjectType.LOGOUT);
User logout2User = new User();
logout2User.setAccount(logoutUser.getAccount());
offObject.setObject(logout2User);
for (OutputThread offOut : map.getAll()) { // 广播用户下线消息
offOut.setMessage(offObject);
}
break ;
case Refresh_FRIEND_LIST: // 更新好友
List<User> refreshList =
dao.refreshFriendList(transObj.getFromUser().getAccount());
TransObject <List<User>> refreshMsg
= new TransObject<> (TransObjectType.Refresh_FRIEND_LIST);
refreshMsg.setObject(refreshList);
outThread.setMessage(refreshMsg);
break ;
}
}
}
}
其中,线程池那部分的使用,我觉得很有用,我要特别留意一下
public ServerManager() {
try {
// 创建线程池,池中具有(cpu个数*50)条线程
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors() * 50 );
serverSocket = new ServerSocket(Constants.SERVER_PORT);
System.out.println( "服务器IP="
+ Inet4Address.getLocalHost().getHostAddress());
} catch (Exception e) {
e.printStackTrace();
exit();
}
}
try {
while (isStart) {
socket = serverSocket.accept();
String ip = socket.getRemoteSocketAddress().toString();
System.out.println(DateHelper.getDateByCN()
+ " 用户:" + ip + " 已建立连接" );
// 为支持多用户并发访问,采用线程池管理每一个用户的连接请求
if (socket.isConnected()) {
executorService.execute( new SocketTask(socket)); // 添加到线程池
}
}
}
分类: Java 笔记
标签: Java Swing Socket
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于JAVA 即时网络通信我的服务器的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did46726