Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

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

$
0
0

实现分析、

1.开启服务端

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

2.用户聊天

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

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

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


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

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


服务端界面:


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

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

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



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>