Quantcast
Viewing all articles
Browse latest Browse all 5930

JAVA进阶案例 TCP编程之网络聊天工具(服务端)

实现分析、

1.开启服务端

客户端选择‘登录以后’后,提示输入用户名和密码,验证成功则进入好友列表界面

2.用户聊天

双击好友,进入好友聊天界面。在信息框编辑信息 点击发送 

当客户端向服务端发送数据时  根据指令的不同来处理不同的业务。

如果是登录,服务端向数据库查询用户信息。然后把处理后的信息发送给服务端。

Image may be NSFW.
Clik here to view.

如果是聊天业务,首先判断当前好友是否在线

如果好友不在线,处理后的信息发送给发送者,如果好友在线 处理后的信息发送给接收者(朋友)

Image may be NSFW.
Clik here to view.

服务端界面:

Image may be NSFW.
Clik here to view.

简单介绍一下 服务端的运行流程

当点击开启服务后 新建一个线程开启服务器循环监听客户端的连接

try {
			ServerSocket ss = new ServerSocket(4096);
			Socket socket = null;
			// 循环监听客户端的连接 每连接一个客户端 就为其实例化一个线程
			// 在该线程 I/O阻塞监听客户端发送的信息 不然你只能收到一次信息~
			while (true) {
				socket = ss.accept();
				ServiceThread thread = new ServiceThread(socket);
				thread.start();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

至于为何要新建线程开启服务,归结于while循环里面的ss.accept();  如果一直没接收到客户端的连接,会导致线程阻塞。

如果你使用的还是main线程,那么关闭服务会无法点击。

而为了实现多客户端的连接  所以在上面我们为每一个客户端都开启一个线程。

线程的run方法为

	@Override
	public void run() {
		ObjectInputStream ois = null;
		ObjectOutputStream oos = null;
		// 时刻监听 客户端发送来的数据
		while (socket != null) {
			try {
				ois = new ObjectInputStream(socket.getInputStream());
				CommandTranser msg = (CommandTranser) ois.readObject();
				// 处理客户端发送来的信息
				msg = execute(msg);
				if ("message".equals(msg.getCmd())) {
					/*
					 * 如果 msg.ifFlag即 服务器处理成功 可以向朋友发送信息 如果服务器处理信息失败 信息发送给发送者本人
					 */
					if (msg.isFlag()) {
						oos = new ObjectOutputStream(SocketList.getSocket(
								msg.getReceiver()).getOutputStream());
					} else {
						oos = new ObjectOutputStream(socket.getOutputStream());
					}
				}
				// 如果是登录请求 发送给发送者本人
				if ("login".equals(msg.getCmd())) {
					oos = new ObjectOutputStream(socket.getOutputStream());
				}
				oos.writeObject(msg);
			} catch (IOException e) {
				socket = null;
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}
也是一个while循环  I/O阻塞接收客户端的信息。因为你不能只接收客户端一次的信息吧  

如果你发现你只能接受一次信息  问题就出在这里了   因为我也在这里错了~~~~~~
在这里出现了CommandTranser类,execute(CommandTranser msg)方法和SocketList类里面的静态方法getSocket

SocketList类很简单  主要是保存已经和服务器成功连接的客户端

package cn.edu.xynu.util;

import java.net.Socket;
import java.util.HashMap;

import cn.edu.xynu.entity.SocketThread;



/**
 * @author scx
 *	所有已经成功登录服务器的socket和昵称
 */
public class SocketList {
	private static HashMap<String, Socket> map=new HashMap<String, Socket>();
	//将SocketThread入集合
	public static void addSocket(SocketThread socketThread){
		map.put(socketThread.getName(), socketThread.getSocket());
	}
	//通过昵称返回socket
	public static Socket getSocket(String name){
		return map.get(name);
	}
}
SocketThread实体类

所有成功连接客户端的socket实体类 包括一个socket (客户端)和昵称

package cn.edu.xynu.entity;

import java.net.Socket;

/**
 * @author scx 所有成功连接客户端的socket实体类 包括一个socket 一个昵称
 */
public class SocketThread {
	private Socket socket;
	private String name;

	public SocketThread() {
		super();
		// TODO Auto-generated constructor stub
	}

	public SocketThread(Socket socket, String name) {
		super();
		this.socket = socket;
		this.name = name;
	}

	public Socket getSocket() {
		return socket;
	}

	public void setSocket(Socket socket) {
		this.socket = socket;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

CommandTranser这个类主要负责服务端和客户端的通信数据。

具体代码及注释如下:

package cn.edu.xynu.util;

import java.io.Serializable;

/**
 * @author scx
 *	客户端与服务器交互的数据
 */
public class CommandTranser implements Serializable{
	private static final long serialVersionUID = 1L;
	private String sender=null;//发送者
	private String receiver=null;//接受者
	private Object data=null;//传递的数据
	private boolean flag=false;//指令的处理结果
	private String cmd=null;//服务端要做的指令
	private String result=null;//处理结果
	
	public String getSender() {
		return sender;
	}
	public void setSender(String sender) {
		this.sender = sender;
	}
	public String getReceiver() {
		return receiver;
	}
	public void setReceiver(String receiver) {
		this.receiver = receiver;
	}
	public Object getData() {
		return data;
	}
	public void setData(Object data) {
		this.data = data;
	}
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public String getResult() {
		return result;
	}
	public void setResult(String result) {
		this.result = result;
	}
	public String getCmd() {
		return cmd;
	}
	public void setCmd(String cmd) {
		this.cmd = cmd;
	}
	
}
execute(CommandTranser msg)方法主要处理客户端向服务端发送的数据

由于我们只实现了两个功能  即登录 和聊天 

所以execute方法主要代码如下

// 如果是登录请求
		if ("login".equals(msg.getCmd())) {
			UserService userService = new UserService();
			User user = (User) msg.getData();
			msg.setFlag(userService.checkUser(user));
			/*
			 * 如果登陆成功,将该客户端加入已经连接成功的map集合里面 并且开启此用户的接受线程
			 */
			if (msg.isFlag()) {
				// 将该线程加入连接成功的map集合
				SocketThread socketThread = new SocketThread();
				socketThread.setName(msg.getSender());
				socketThread.setSocket(socket);
				SocketList.addSocket(socketThread);
				msg.setResult("登陆成功");
			} else {
				msg.setResult("登录失败");
			}
		}

		// 如果是发送消息的指令 判断当前用户是否在线

		if ("message".equals(msg.getCmd())) {
			// 如果要发送的用户在线 发送信息
			if (SocketList.getSocket(msg.getReceiver()) != null) {
				msg.setFlag(true);
			} else {
				msg.setFlag(false);
				msg.setResult("当前用户不在线");
			}
		}
		return msg;
	}

当用户为login请求时,通过userservice查询数据库中是否有此用户,并返回结果

package cn.edu.xynu.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import cn.edu.xynu.entity.User;
import cn.edu.xynu.util.DBHelper;

/**
 * @author scx 客户的业务处理 只查询用户是否在数据库中存在
 */
public class UserService {
	public boolean checkUser(User user) {
		PreparedStatement stmt = null;
		Connection conn = null;
		ResultSet rs = null;
		conn = DBHelper.getConnection();
		String sql = "select * from user where username=? and password =?";
		try {
			stmt = conn.prepareStatement(sql);
			stmt.setString(1, user.getUsername());
			stmt.setString(2, user.getPassword());
			rs = stmt.executeQuery();
			if (rs.next()) {
				return true;
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (stmt != null)
					stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return false;
	}
}
User类用户的实体类,只包括帐号 密码信息

package cn.edu.xynu.entity;

import java.io.Serializable;

/**
 * @author scx
 *	用户信息的实体类
 */
public class User implements Serializable{
	private static final long serialVersionUID = 1L;
	private String username;
	private String password;
	
	public User() {
		super();
	}

	public User(String username, String password) {
		super();
		this.username = username;
		this.password = password;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
}

而DBhelpher为连接数据库工具类

package cn.edu.xynu.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @author scx
 *	数据库
 */
public class DBHelper {
	private static final String driver="com.mysql.jdbc.Driver";
	private static final String url="jdbc:mysql://localhost:3306/myqquser?useUnicode=true&charcterEncoding=UTF-8";
	private static final String username="root";
	private static final String password="";
	private static  Connection con=null;
	//静态块代码负责加载驱动
	static
	{
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static Connection getConnection(){

		if(con==null){
			try {
				con=DriverManager.getConnection(url, username, password);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return con;
	}
}	
下一篇  
JAVA进阶案例 TCP编程之网络聊天工具(客户端)

全部源码点击下载



作者:su20145104009 发表于2016/10/22 21:04:45 原文链接
阅读:115 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles