当前位置: 首页 > news >正文

芒果tv网站建设的目标5g国产天天5g天天

芒果tv网站建设的目标,5g国产天天5g天天,网站结构怎么分析,专业关键词优化平台文章目录 1.项目所用技术栈本项目使用了java基础#xff0c;面向对象#xff0c;集合#xff0c;泛型#xff0c;IO流#xff0c;多线程#xff0c;Tcp字节流编程的技术 2.通信系统整体分析主要思路#xff08;自己理解#xff09;1.如果不用多线程2.使用多线程3.对多线… 文章目录 1.项目所用技术栈本项目使用了java基础面向对象集合泛型IO流多线程Tcp字节流编程的技术 2.通信系统整体分析主要思路自己理解1.如果不用多线程2.使用多线程3.对多线程的新理解 3.功能实现——用户登录1.实现传输数据的三个类Message和User和MessageType1.首先创建两个模块QQSever和QQClient2.完成两个模块共有类的编写 2.实现用户登录界面框架1.导入工具类utils/Utility.java2.编写基本用户界面view/QQView.java 3.实现客户端的登录部分1.qqclient/service/UserClientService.java2.qqclient/service/ManageClientConnectServerThread.java3.qqclient/service/ClientConnectServerThread.java4.修改QQView.java中的验证用户是否合法语句 4.实现服务器端的登录部分1.qqserver/service/QQServer.java2.qqserver/service/ServerConnectClientThread.java 5.登录阶段运行调试过程1.第一次运行报错用户名密码正确时解决方法2.第二次运行报错用户名密码不正确时原因解决方法 6.实现多个合法用户可以登录qqserver/service/QQServer.java更新 4.功能实现——拉取在线用户1.功能完成1.qqcommon/MessageType.java更新2.qqclient/service/ClientConnectServerThread.java更新3.qqclient/service/UserClientService.java更新4.view/QQView.java更新5.qqserver/service/ManageClientThreads.java更新添加方法 6.qqserver/service/QQServer.java更新添加方法 7.qqserver/service/ServerConnectClientThread.java更新try语句更新 2.调试阶段1.代码冗余2.线程同步问题 5.功能实现——无异常退出系统1.功能完成1.qqcommon/MessageType.java更新2.qqclient/service/ClientConnectServerThread.java更新try语句更新 3.qqclient/service/UserClientService.java更新添加三个方法 4.view/QQView.java更新5.qqserver/service/QQServer.java更新添加两个方法 6.qqserver/service/ManageClientThreads.java更新添加方法 7.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段1.出现空指针异常2.数据未同步3.安全性提升 6.功能实现——私聊功能1.功能完成1.qqclient/service/ClientConnectServerThread.java更新2.qqclient/service/UserClientService.java更新添加方法 3.view/QQView.java更新4.qqserver/service/QQServer.java更新添加方法 5.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段并未发现错误 7.功能实现——群发功能1.功能完成1.qqcommon/MessageType.java更新2.qqclient/service/ClientConnectServerThread.java更新3.qqclient/service/UserClientServer.java更新添加方法 4.qqserver/service/QQServer.java更新5.qqserver/service/QQServer.java更新添加方法 6.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段未发现错误 8.功能实现——发文件1.功能完成1.qqcommon/MessageType.java更新2.qqcommon/Message.java更新3.qqclient/service/ClientConnectServerThread.java更新4.qqclient/service/UserClientServer.java更新添加方法 5.view/QQView.java更新6.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段1.传输文件大小膨胀 9.功能实现——服务器端推送新闻1.功能完成1.qqserver/service/SendAllThread.java2.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段1.子线程群发问题 1.项目所用技术栈 本项目使用了java基础面向对象集合泛型IO流多线程Tcp字节流编程的技术 2.通信系统整体分析 主要思路自己理解 1.如果不用多线程 客户端A连接并发送消息服务端B通过 accept 方法接受客户端A的连接然后读取数据。服务端处理并响应服务端B处理客户端A的数据发送响应然后继续监听新的消息或关闭连接。如果服务器继续监听来自A的数据它将继续阻塞在读操作上。客户端A不再发送数据如果客户端A在发送了一些数据之后停止发送并且服务器端正在等待读取更多数据这时服务端将阻塞在对A的读操作上因为它正在等待A发送更多数据。客户端B尝试连接由于服务端B正在处理客户端A的连接并阻塞在读操作上它无法接受客户端B的连接请求。直到服务端B处理完A的请求并返回到 accept 方法客户端B才能连接。 2.使用多线程 客户端A向服务器端B建立连接连接成功客户端A和服务器端各自有一个socket客户端A向服务器发送User对象包含用户名和密码服务器端获取内容并验证验证结束之后将结果返回给客户端A客户端A收到结果之后如果登录成功则开启一个子线程将socket放进去使得子线程能够对其进行操作然后子线程一直读取通道中的信息如果没有信息则会阻塞。而主线程则会继续执行界面的操作两者互不干涉此时服务器端则会也开启一个线程将socket放到线程中然后持续读取与客户端A通道中的信息以执行特定的操作然后服务器端的主线程会继续进行监听如果有其他的客户端链接则直接连接上此时客户端B链接服务器端服务器端提供链接并且验证User如果正确则服务器端再开一个线程执行跟上面同样的操作而主线程依然继续监听这样就实现了多用户连接。 3.对多线程的新理解 多线程就相当于一个独立于主线程之外可以运行的实例中的run方法主线程可以实例化为多个子线程然后调用run方法对当前实例进行操作当仅仅靠主线程无法实现目标时就要使用多线程并发执行单独开一个线程执行特定的任务多线程的设计首先要明确这个线程要完成什么功能需要给他传递什么属性然后就可以开始设计这个单线程最后还要考虑这个线程是不是要并发执行如果要并发执行则就要考虑对象锁或者类锁实现同步 3.功能实现——用户登录 1.实现传输数据的三个类Message和User和MessageType 1.首先创建两个模块QQSever和QQClient 2.完成两个模块共有类的编写 qqcommon/Message.java package qqcommon;import java.io.Serializable;/*** author 孙显圣* version 1.0* 表示客户端和服务器端通讯时的消息对象*/ public class Message implements Serializable { //也需要进行序列化private String sender; //发送者private String getter; //接受者private String content; //消息内容private String sendTime; //发送时间private String mesType; //消息类型在接口中定义已知的消息类型public String getSender() {return sender;}public void setSender(String sender) {this.sender sender;}public String getGetter() {return getter;}public void setGetter(String getter) {this.getter getter;}public String getContent() {return content;}public void setContent(String content) {this.content content;}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;} } qqcommon/MessageType.java package qqcommon;/*** author 孙显圣* version 1.0*/ public interface MessageType {//在接口中定义了不同的常量//不同常量的值表示不同的消息类型String MESSAGE_LOGIN_SUCCEED 1; //表示登录成功String MESSAGE_LOGIN_FAIL 2; //表示登录失败 } qqcommon/User.java package qqcommon;import java.io.Serializable;/*** author 孙显圣* version 1.0* 表示一个用户/客户信息*/ public class User implements Serializable { //由于需要序列化所以需要实现接口private String userId; //用户名private String passwd; //密码public User() {}public User(String userId, String passwd) {this.userId userId;this.passwd 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;} } 2.实现用户登录界面框架 1.导入工具类utils/Utility.java package utils;/**工具类的作用:处理各种情况的用户输入并且能够按照程序员的需求得到用户的控制台输入。 */import java.util.Scanner;/***/ public class Utility {//静态属性。。。private static Scanner scanner new Scanner(System.in);/*** 功能读取键盘输入的一个菜单选项值1——5的范围* return 1——5*/public static char readMenuSelection() {char c;for (; ; ) {String str readKeyBoard(1, false);//包含一个字符的字符串c str.charAt(0);//将字符串转换成字符char类型if (c ! 1 c ! 2 c ! 3 c ! 4 c ! 5) {System.out.print(选择错误请重新输入);} else break;}return c;}/*** 功能读取键盘输入的一个字符* return 一个字符*/public static char readChar() {String str readKeyBoard(1, false);//就是一个字符return str.charAt(0);}/*** 功能读取键盘输入的一个字符如果直接按回车则返回指定的默认值否则返回输入的那个字符* param defaultValue 指定的默认值* return 默认值或输入的字符*/public static char readChar(char defaultValue) {String str readKeyBoard(1, true);//要么是空字符串要么是一个字符return (str.length() 0) ? defaultValue : str.charAt(0);}/*** 功能读取键盘输入的整型长度小于2位* return 整数*/public static int readInt() {int n;for (; ; ) {String str readKeyBoard(10, false);//一个整数长度10位try {n Integer.parseInt(str);//将字符串转换成整数break;} catch (NumberFormatException e) {System.out.print(数字输入错误请重新输入);}}return n;}/*** 功能读取键盘输入的 整数或默认值如果直接回车则返回默认值否则返回输入的整数* param defaultValue 指定的默认值* return 整数或默认值*/public static int readInt(int defaultValue) {int n;for (; ; ) {String str readKeyBoard(10, true);if (str.equals()) {return defaultValue;}//异常处理...try {n Integer.parseInt(str);break;} catch (NumberFormatException e) {System.out.print(数字输入错误请重新输入);}}return n;}/*** 功能读取键盘输入的指定长度的字符串* param limit 限制的长度* return 指定长度的字符串*/public static String readString(int limit) {return readKeyBoard(limit, false);}/*** 功能读取键盘输入的指定长度的字符串或默认值如果直接回车返回默认值否则返回字符串* param limit 限制的长度* param defaultValue 指定的默认值* return 指定长度的字符串*/public static String readString(int limit, String defaultValue) {String str readKeyBoard(limit, true);return str.equals()? defaultValue : str;}/*** 功能读取键盘输入的确认选项Y或N* 将小的功能封装到一个方法中.* return Y或N*/public static char readConfirmSelection() {System.out.println(请输入你的选择(Y/N): 请小心选择);char c;for (; ; ) {//无限循环//在这里将接受到字符转成了大写字母//y Y nNString str readKeyBoard(1, false).toUpperCase();c str.charAt(0);if (c Y || c N) {break;} else {System.out.print(选择错误请重新输入);}}return c;}/*** 功能 读取一个字符串* param limit 读取的长度* param blankReturn 如果为true ,表示 可以读空字符串。 * 如果为false表示 不能读空字符串。* * 如果输入为空或者输入大于limit的长度就会提示重新输入。* return*/private static String readKeyBoard(int limit, boolean blankReturn) {//定义了字符串String line ;//scanner.hasNextLine() 判断有没有下一行while (scanner.hasNextLine()) {line scanner.nextLine();//读取这一行//如果line.length0, 即用户没有输入任何内容直接回车if (line.length() 0) {if (blankReturn) return line;//如果blankReturntrue,可以返回空串else continue; //如果blankReturnfalse,不接受空串必须输入内容}//如果用户输入的内容大于了 limit就提示重写输入 //如果用户如的内容 0 limit ,我就接受if (line.length() 1 || line.length() limit) {System.out.print(输入长度不能大于 limit 错误请重新输入);continue;}break;}return line;} } 2.编写基本用户界面view/QQView.java package view;import utils.Utility;/*** author 孙显圣* version 1.0* 客户端的菜单界面*/ public class QQView {public static void main(String[] args) {new QQView().mainMenu();}private boolean loop true; //控制主菜单循环执行//显示主菜单的方法private void mainMenu() {while (loop) { //循环显示菜单System.out.println(欢迎登录网络通信系统);System.out.println( 1 登录系统);System.out.println( 9 退出系统);System.out.print(请输入您的选择);String s Utility.readString(1); //读取一个字符//根据选择执行操作switch (s) {case 1:System.out.println(请输入用户号);String userId Utility.readString(50);System.out.println(请输入密 码);String passwd Utility.readString(50);//去服务端验证该用户是否合法//1.假设合法if (false) {//循环输出菜单while (loop) {System.out.println(网络通信系统二级菜单);System.out.println( 1 显示在线用户列表);System.out.println( 2 群发消息);System.out.println( 3 私聊消息);System.out.println( 4 发送文件);System.out.println( 9 退出系统);System.out.print(请输入您的选择);String key Utility.readString(1);//根据选择做出相应操作switch (key) {case 1:System.out.println(显示在线用户列表);break;case 2:System.out.println(群发消息);break;case 3:System.out.println(私聊消息);break;case 4:System.out.println(发送文件);break;case 9:System.out.println(用户退出系统);loop false;break;}}}//2.不合法else {//退出这个switchSystem.out.println(用户名或密码不正确);break;}break;case 9:System.out.println(用户退出系统);loop false;break;}}} } 3.实现客户端的登录部分 1.qqclient/service/UserClientService.java package qqclient.service;import com.sun.org.apache.xpath.internal.operations.Variable; import qqcommon.Message; import qqcommon.MessageType; import qqcommon.User;import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException;/*** author 孙显圣* version 1.0* 完成用户登录验证和用户注册等等功能*/ public class UserClientService {private User user new User(); //由于可能在其他地方需要使用到这个User对象所以将其设置为这个类的属性//根据前端输入的用户名和密码封装成User对象并且发送到服务器端接受服务器端返回的Message对象并根据mesType来确定是否符合要求public boolean checkUser(String userId, String pwd) throws IOException, ClassNotFoundException {//设置一个临时变量用于返回值boolean res false;//将用户名和密码封装到User对象中user.setUserId(userId);user.setPasswd(pwd);//获取客户端的socketSocket socket new Socket(InetAddress.getLocalHost(), 9999);//获取客户端的输出流OutputStream outputStream socket.getOutputStream();//将其转换成对象处理流ObjectOutputStream objectOutputStream new ObjectOutputStream(outputStream);//将user对象发送objectOutputStream.writeObject(user);//获取从服务器端回复的Message对象//获取客户端的输入流InputStream inputStream socket.getInputStream();//转换为对象处理流ObjectInputStream objectInputStream new ObjectInputStream(inputStream);//读取message对象Message o (Message) objectInputStream.readObject(); //此时我们确定读取的一定是Message对象所以将其向下转型//根据获取的mesType来确定是否成功if (o.getMesType().equals(MessageType.MESSAGE_LOGIN_SUCCEED)) {//创建一个和服务器端保持通信的线程ClientConnectServerThread clientConnectServerThread new ClientConnectServerThread(socket);//启动客户端的线程使其等待服务器的信息clientConnectServerThread.start();//为了后面客户端的扩展放到一个集合中ManageClientConnectServerThread.addClientConnectServerThread(userId, clientConnectServerThread);//成功了将返回值设置为trueres true;} else {//如果登录失败则虽然没有启动线程但是还是开启了一个socket所以要关闭socket.close();}return res;}} 2.qqclient/service/ManageClientConnectServerThread.java package qqclient.service;import java.util.HashMap;/*** author 孙显圣* version 1.0* 该类管理客户端连接到服务器端的线程的类*/ public class ManageClientConnectServerThread {//把多个线程放到一个HashMap的集合中key是用户idvalue是线程private static HashMapString, ClientConnectServerThread hm new HashMap();//将某个线程放到集合中public static void addClientConnectServerThread(String userId, ClientConnectServerThread clientConnectServerThread) {hm.put(userId, clientConnectServerThread);}//通过userId可以得到该线程public static ClientConnectServerThread getClientConnectServerThread(String userId) {return hm.get(userId);}} 3.qqclient/service/ClientConnectServerThread.java package qqclient.service;import qqcommon.Message;import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket;/*** author 孙显圣* version 1.0* 这个线程持有socket*/ public class ClientConnectServerThread extends Thread {private Socket socket;//该构造器可以接受一个Socket对象public ClientConnectServerThread(Socket socket) {this.socket socket;}//更方便的得到Socketpublic Socket getSocket() {return socket;}//因为线程需要在后台一直保持和服务器的通信因此使用while循环Overridepublic void run() {while (true) {System.out.println(客户端线程等待读取从服务器端发送的信息);try {//获取该线程socket的对象输入流ObjectInputStream objectInputStream new ObjectInputStream(socket.getInputStream());//读取信息Message o (Message) objectInputStream.readObject(); //如果没有数据传进来则这个线程则会阻塞} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}} } 4.修改QQView.java中的验证用户是否合法语句 4.实现服务器端的登录部分 1.qqserver/service/QQServer.java package qqserver.service;import qqcommon.Message; import qqcommon.MessageType; import qqcommon.User;import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket;/*** author 孙显圣* version 1.0* 这是服务器监听9999等待客户端的连接并且保持通信*/ public class QQServer {private ServerSocket ss null;public QQServer() {System.out.println(服务端在9999端口监听。。。);try {ss new ServerSocket(9999); //开一个9999端口监听User对象} catch (IOException e) {throw new RuntimeException(e);}//由于可能会有很多的客户端发送信息所以要使用循环监听并且返回不同的sockettry {while (true) {//每次有用户连接都获取socketSocket socket ss.accept();//读取客户端的User对象ObjectInputStream objectInputStream new ObjectInputStream(socket.getInputStream());User o (User) objectInputStream.readObject();//创建一个Message用于回复客户端Message message new Message();//输出流ObjectOutputStream objectOutputStream null;//对其进行验证先写死if (o.getUserId().equals(100) o.getPasswd().equals(123456)) {message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);//获取输出流回复客户端objectOutputStream new ObjectOutputStream(socket.getOutputStream());objectOutputStream.writeObject(message);//回复完客户端之后需要创建一个线程用来管理socket用来保持与客户端的通信ServerConnectClientThread serverConnectClientThread new ServerConnectClientThread(socket, o.getUserId());serverConnectClientThread.start();//使用集合来管理线程ManageClientThreads.addClientThread(o.getUserId(), serverConnectClientThread);}else {//如果登录失败就不能启动线程将失败的消息返回给客户端则关闭socketmessage.setMesType(MessageType.MESSAGE_LOGIN_FAIL);objectOutputStream.writeObject(message);socket.close();}}} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} finally {try {//如果最终退出了循环说明不再需要服务器端监听所以关闭ServerSocketss.close();} catch (IOException e) {throw new RuntimeException(e);}}} } 2.qqserver/service/ServerConnectClientThread.java package qqserver.service;import qqcommon.Message;import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket;/*** author 孙显圣* version 1.0* 该类对应的一个对象和某个客户端保持连接*/ public class ServerConnectClientThread extends Thread{//管理一个socket,和对应的用户idprivate Socket socket;private String userId;public ServerConnectClientThread(Socket socket, String userId) {this.socket socket;this.userId userId;}//保持这个socket的运行Overridepublic void run() {while (true) {System.out.println(服务端和客户端保持通信读取数据。。。);try {//读取数据ObjectInputStream objectInputStream new ObjectInputStream(socket.getInputStream());Message o (Message) objectInputStream.readObject(); //由于之前已经接受过User对象了现在就是接受的Message对象} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}} } 5.登录阶段运行调试过程 1.第一次运行报错用户名密码正确时 Connect reset。。。。 解决方法 在两个序列化的类添加这行代码private static final long serialVersionUID 1L;修改之后密码正确的时候可以正常显示 2.第二次运行报错用户名密码不正确时 原因 在执行else语句时由于没有运行if所以是空的 解决方法 由于if和else都会用到所以提出来在外边初始化 成功运行 6.实现多个合法用户可以登录 qqserver/service/QQServer.java更新 添加以下内容 //创建一个集合存放多个用户如果是这些用户登录就认为是合法的//可以使用ConcurrentHashMap这样就避免了线程安全问题HashMap线程不安全的private static ConcurrentHashMapString, User vaildUsers new ConcurrentHashMap();//使用静态代码块初始化static {vaildUsers.put(100, new User(100, 123456));vaildUsers.put(200, new User(200, 123456));vaildUsers.put(300, new User(300, 123456));vaildUsers.put(400, new User(400, 123456));}//验证用户是否有效的方法private boolean checkUser(User user) {String userId user.getUserId(); //获取键String passwd user.getPasswd(); //获取密码//过关斩将//首先查找键是否存在if (!vaildUsers.containsKey(userId)) {return false;}if (!vaildUsers.get(userId).getPasswd().equals(passwd)) {return false;}return true;}修改验证逻辑 4.功能实现——拉取在线用户 1.功能完成 1.qqcommon/MessageType.java更新 String MESSAGE_LOGIN_SUCCEED 1; //表示登录成功String MESSAGE_LOGIN_FAIL 2; //表示登录失败String MESSAGE_COMM_MES 3; //普通信息包String MESSAGE_GET_ONLINE_FRIEND 4; //要求返回在线用户列表String MESSAGE_RET_ONLINE_FRIEND 5; //返回在线用户列表String MESSAGE_CLIENT_EXIT 6; //客户端请求退出2.qqclient/service/ClientConnectServerThread.java更新 package qqclient.service;import qqcommon.Message;import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket;/*** author 孙显圣* version 1.0* 这个线程持有socket*/ public class ClientConnectServerThread extends Thread {private Message message; //存放信息private Socket socket;public static Boolean STATE false; //子线程任务完成状态用于线程同步//该构造器可以接受一个Socket对象public ClientConnectServerThread(Socket socket) {this.socket socket;}//更方便的得到Socketpublic Socket getSocket() {return socket;}//刷新子线程状态public static void flushState() {STATE false;}//因为线程需要在后台一直保持和服务器的通信因此使用while循环Overridepublic void run() {while (true) {System.out.println(客户端线程等待读取从服务器端发送的信息);try {//获取该线程socket的对象输入流ObjectInputStream objectInputStream new ObjectInputStream(socket.getInputStream());//读取信息message (Message) objectInputStream.readObject(); //如果没有数据传进来则这个线程则会阻塞switch (message.getMesType()) {case 3: //普通信息包break;case 5: //返回在线用户列表System.out.println(message.getContent());break;}STATE true; //更新状态} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}} } 3.qqclient/service/UserClientService.java更新 //向服务器端发送请求在线用户的数据包public void onlineFriendList(String userId) throws IOException, ClassNotFoundException, InterruptedException {//获取一个消息包Message message new Message();//设置参数message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);//获取当前用户名对应的线程ClientConnectServerThread currentThread ManageClientConnectServerThread.getClientConnectServerThread(userId);//获取线程中的socketSocket socket currentThread.getSocket();//获取对象输出流ObjectOutputStream objectOutputStream new ObjectOutputStream(socket.getOutputStream());//输出对象objectOutputStream.writeObject(message);while (!ClientConnectServerThread.STATE); //等待子线程完成ClientConnectServerThread.flushState(); //刷新状态}4.view/QQView.java更新 5.qqserver/service/ManageClientThreads.java更新 添加方法 //获取线程集合public static HashMapString, ServerConnectClientThread getHm() {return hm;}6.qqserver/service/QQServer.java更新 添加方法 //遍历当前用户列表并发送到前端public static void getCurrentOnlineFriendList(Socket socket) throws IOException {//获取当前用户列表HashMapString, ServerConnectClientThread hm ManageClientThreads.getHm();//遍历并保存到数据包中Message message new Message(); //创建一个数据包//设置数据类型message.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND); //类型为返回在线用户列表//记录返回的内容StringBuilder res new StringBuilder();//获取所有的key使用迭代器遍历SetString strings hm.keySet();IteratorString iterator1 strings.iterator();int i 0; //统计用户个数while (iterator1.hasNext()) {String next iterator1.next();res.append(用户 (i) : ).append(next).append( ); //拼接}//将结果放到数据包中message.setContent(res.toString());//根据目前的socket来发送数据ObjectOutputStream objectOutputStream new ObjectOutputStream(socket.getOutputStream());objectOutputStream.writeObject(message);}7.qqserver/service/ServerConnectClientThread.java更新 try语句更新 //读取数据ObjectInputStream objectInputStream new ObjectInputStream(socket.getInputStream());Message o (Message) objectInputStream.readObject(); //由于之前已经接受过User对象了现在就是接受的Message对象//根据读到的信息类型进行处理switch (o.getMesType()) {case 3: //普通信息包break;case 4: //返回当前在线用户列表QQServer.getCurrentOnlineFriendList(socket); //将目前的socket给他break;case 6: //客户端请求退出break;}2.调试阶段 1.代码冗余 我最开始自己实现时获取服务器端的socket是在线程数组中通过客户端传过来的姓名来获取的后来发现没这么麻烦服务器端的一个线程就对应一个通道的socket并且在不断读取如果读取到了则此时的线程实例中的属性socket就应该是与发送信息的客户端连通的那个socket直接使用就可以了 2.线程同步问题 我在拉取在线用户时在QQ的前端界面调取一个方法来向服务器端发送Message来请求获取在线用户。然后服务器端发送信息给客户端此时的客户端是子线程在接收数据而主线程运行前端页面由于主线程只是发送了个消息就直接退出case进行下一次循环而子线程还要根据信息处理并返回所以一定比主线程慢所以我在子线程里面加了一个布尔型的状态常量并且设置了一个方法可以刷新状态这样在主线程调用的方法中可以使用一个while循环持续等待直到子线程输出数据然后再刷新状态 5.功能实现——无异常退出系统 1.功能完成 1.qqcommon/MessageType.java更新 package qqcommon;/*** author 孙显圣* version 1.0*/ public interface MessageType {//在接口中定义了不同的常量//不同常量的值表示不同的消息类型String MESSAGE_LOGIN_SUCCEED 1; //表示登录成功String MESSAGE_LOGIN_FAIL 2; //表示登录失败String MESSAGE_COMM_MES 3; //普通信息包String MESSAGE_GET_ONLINE_FRIEND 4; //要求返回在线用户列表String MESSAGE_RET_ONLINE_FRIEND 5; //返回在线用户列表String MESSAGE_CLIENT_EXIT 6; //客户端请求退出String MESSAGE_SERVICE_EXIT_SUCCESS 7; //服务器端退出成功 } 2.qqclient/service/ClientConnectServerThread.java更新 try语句更新 //获取该线程socket的对象输入流ObjectInputStream objectInputStream new ObjectInputStream(socket.getInputStream());//读取信息message (Message) objectInputStream.readObject(); //如果没有数据传进来则这个线程则会阻塞switch (message.getMesType()) {case 3: //普通信息包break;case 5: //返回在线用户列表System.out.println(message.getContent());break;case 7: //服务端退出成功new UserClientService().exitAllThreads(socket, objectInputStream); //关闭资源以及退出主线程loop false; //退出线程循环break;}STATE true; //更新状态3.qqclient/service/UserClientService.java更新 添加三个方法 //向客户端发送信数据包的方法public void sendMessageToService(String userId, Message message) throws IOException {//获取当前线程ClientConnectServerThread clientConnectServerThread ManageClientConnectServerThread.getClientConnectServerThread(userId);//获取socketSocket socket clientConnectServerThread.getSocket();//创建输出流ObjectOutputStream objectOutputStream new ObjectOutputStream(socket.getOutputStream());//发送信息objectOutputStream.writeObject(message);}//向客户端发送请求退出的信息public void requestExit(String userId) throws IOException {//创建一个MessageMessage message new Message();message.setMesType(MessageType.MESSAGE_CLIENT_EXIT);message.setSender(userId); //告诉服务器端发送者是谁这样可以清除集合中的线程//发送数据包sendMessageToService(userId, message);}//退出子线程以及主线程public void exitAllThreads(Socket socket, ObjectInputStream objectInputStream) throws IOException {objectInputStream.close();socket.close();System.exit(0);}4.view/QQView.java更新 5.qqserver/service/QQServer.java更新 添加两个方法 //服务器端发送给客户端数据包的方法public static void sendToClientMessage(Socket socket, Message message) throws IOException {//获取输出流ObjectOutputStream objectOutputStream new ObjectOutputStream(socket.getOutputStream());objectOutputStream.writeObject(message);}//服务器端返回一个退出成功的数据包然后关闭socketpublic static void ServiceExit(Socket socket, ObjectInputStream objectInputStream) throws IOException {//创建一个数据包Message message new Message();//放入数据message.setMesType(MessageType.MESSAGE_SERVICE_EXIT_SUCCESS); //服务器端退出成功//发送sendToClientMessage(socket, message);objectInputStream.close();socket.close();}6.qqserver/service/ManageClientThreads.java更新 添加方法 //根据userId删除public static void deleteByUserId(String userId) {hm.remove(userId);}7.qqserver/service/ServerConnectClientThread.java更新 //保持这个socket的运行private boolean loop true;Overridepublic void run() {while (loop) {System.out.println(服务端和客户端 userId 线程保持通信,读取数据。。。);try {//读取数据ObjectInputStream objectInputStream new ObjectInputStream(socket.getInputStream());Message o (Message) objectInputStream.readObject(); //由于之前已经接受过User对象了现在就是接受的Message对象//根据读到的信息类型进行处理switch (o.getMesType()) {case 3: //普通信息包break;case 4: //返回当前在线用户列表QQServer.getCurrentOnlineFriendList(socket); //将目前的socket给他break;case 6: //客户端请求退出ManageClientThreads.deleteByUserId(o.getSender()); //清除列表元素QQServer.ServiceExit(socket, objectInputStream);//关闭流和套接字loop false;break;}} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}}2.调试阶段 1.出现空指针异常 我第一次在关闭客户端的时候在服务器端出现了空指针异常原因我在处理关闭服务器端的时候只是关闭了流和套接字并没有关闭run方法的循环导致子线程继续在读但是由于套接字已经关闭读的时候还要使用它获取流所以出现异常 2.数据未同步 修改完异常之后可以正常退出但是我在测试拉取在线用户时出现了异常原因客户端已经退出但是服务器端的线程集合中的元素并没有清除所以导致了异常 3.安全性提升 原来的的退出系统逻辑就是客户端向服务器端发送退出的请求然后服务器端收到请求就直接退出这样是不安全的因为客户端的主线程向服务器端发送完请求之后就直接退出但是有个问题如果服务器端接受到信息的速度慢了一点导致客 户端先关闭了socket那么服务器端在使用socket的时候就会报异常我的解决方案让客户端通知服务器端请求关闭连接的时候在服务器的socket关闭之前向客户端发送一条消息就是服务器端关闭成功当客户端接收到这个消息的时候再退出 6.功能实现——私聊功能 1.功能完成 1.qqclient/service/ClientConnectServerThread.java更新 2.qqclient/service/UserClientService.java更新 添加方法 //私聊消息public void privateMessages(String sender) throws IOException {//展示所有用户之后//获取用户名称System.out.print(请输入你要聊天的用户名称);String getter new Scanner(System.in).next();//获取聊天的内容System.out.print(请输入聊天的内容);String content new Scanner(System.in).nextLine();//创建一个数据包Message message new Message();message.setMesType(MessageType.MESSAGE_COMM_MES); //普通消息message.setContent(content);message.setSender(sender);message.setGetter(getter);//发送到服务器端sendMessageToService(sender, message);}//读取私聊消息public void readPrivateMessage(Message message) {String sender message.getSender();String content message.getContent();System.out.println(\n sender 对你说 );System.out.println(content);}3.view/QQView.java更新 4.qqserver/service/QQServer.java更新 添加方法 //转发消息public static void forwordMessage(Message message) throws IOException {//获取信息String content message.getContent();String sender message.getSender();String getter message.getGetter();//根据姓名获取线程ServerConnectClientThread sendThread ManageClientThreads.getServerConnectClientThread(getter);//发送包sendToClientMessage(sendThread.getSocket(), message);}5.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段 并未发现错误 7.功能实现——群发功能 1.功能完成 1.qqcommon/MessageType.java更新 package qqcommon;/*** author 孙显圣* version 1.0*/ public interface MessageType {//在接口中定义了不同的常量//不同常量的值表示不同的消息类型String MESSAGE_LOGIN_SUCCEED 1; //表示登录成功String MESSAGE_LOGIN_FAIL 2; //表示登录失败String MESSAGE_COMM_MES 3; //普通信息包String MESSAGE_GET_ONLINE_FRIEND 4; //要求返回在线用户列表String MESSAGE_RET_ONLINE_FRIEND 5; //返回在线用户列表String MESSAGE_CLIENT_EXIT 6; //客户端请求退出String MESSAGE_SERVICE_EXIT_SUCCESS 7; //服务器端退出成功String MESSAGE_SEND_ALL_USER 8; //群发消息 } 2.qqclient/service/ClientConnectServerThread.java更新 3.qqclient/service/UserClientServer.java更新 添加方法 //群发消息public void sendToAllUser(String userId) throws IOException {System.out.println(请输入你要发送的内容);Scanner scanner new Scanner(System.in);String content scanner.nextLine();//创建一个数据包Message message new Message();message.setMesType(MessageType.MESSAGE_SEND_ALL_USER);message.setContent(content);message.setSender(userId);//发送数据包sendMessageToService(userId, message);}//读取群发消息public void readAllSendMessage(Message message) {//获取信息String sender message.getSender();String content message.getContent();System.out.println(\n sender 的群发消息);System.out.println(content);}4.qqserver/service/QQServer.java更新 5.qqserver/service/QQServer.java更新 添加方法 //群发消息public static void sendToAllUser(Message message, String userId) throws IOException {//遍历在线用户集合,发送消息HashMapString, ServerConnectClientThread hm ManageClientThreads.getHm();CollectionServerConnectClientThread threads hm.values();for (ServerConnectClientThread thread : threads) {if (hm.get(userId) thread) { //不用发送给本用户continue;}//发送包sendToClientMessage(thread.getSocket(), message);}}6.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段 未发现错误 8.功能实现——发文件 1.功能完成 1.qqcommon/MessageType.java更新 package qqcommon;/*** author 孙显圣* version 1.0*/ public interface MessageType {//在接口中定义了不同的常量//不同常量的值表示不同的消息类型String MESSAGE_LOGIN_SUCCEED 1; //表示登录成功String MESSAGE_LOGIN_FAIL 2; //表示登录失败String MESSAGE_COMM_MES 3; //普通信息包String MESSAGE_GET_ONLINE_FRIEND 4; //要求返回在线用户列表String MESSAGE_RET_ONLINE_FRIEND 5; //返回在线用户列表String MESSAGE_CLIENT_EXIT 6; //客户端请求退出String MESSAGE_SERVICE_EXIT_SUCCESS 7; //服务器端退出成功String MESSAGE_SEND_ALL_USER 8; //群发消息String MESSAGE_SEND_FILE 9; //发送文件 } 2.qqcommon/Message.java更新 package qqcommon;import java.io.Serializable;/*** author 孙显圣* version 1.0* 表示客户端和服务器端通讯时的消息对象*/ public class Message implements Serializable { //也需要进行序列化private String sender; //发送者private String getter; //接受者private String content; //消息内容private String sendTime; //发送时间private String mesType; //消息类型在接口中定义已知的消息类型private String path; //记录路径private byte[] bytes; //存储文件private int length; //记录长度public int getLength() {return length;}public void setLength(int length) {this.length length;}private static final long serialVersionUID 1L;public byte[] getBytes() {return bytes;}public void setBytes(byte[] bytes) {this.bytes bytes;}public String getPath() {return path;}public void setPath(String path) {this.path path;}public String getSender() {return sender;}public void setSender(String sender) {this.sender sender;}public String getGetter() {return getter;}public void setGetter(String getter) {this.getter getter;}public String getContent() {return content;}public void setContent(String content) {this.content content;}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;} } 3.qqclient/service/ClientConnectServerThread.java更新 4.qqclient/service/UserClientServer.java更新 添加方法 //发送文件public void sendFile(String setter) throws IOException {//获取用户名称System.out.print(请输入要发送文件的用户名称);Scanner scanner new Scanner(System.in);String getter scanner.next();//获取本地文件路径System.out.print(请输入本地文件路径);String path1 scanner.next();//获取对方文件路径System.out.print(请输入对方文件路径);String path2 scanner.next();//读取本地文件BufferedInputStream inputStream new BufferedInputStream(new FileInputStream(path1));//设置缓冲byte[] bytes new byte[1024 * 10];//记录长度int len 0;while ((len inputStream.read(bytes)) ! -1) {Message message new Message();//创建一个数据包message.setSender(setter);message.setGetter(getter);message.setMesType(MessageType.MESSAGE_SEND_FILE);message.setPath(path2);message.setBytes(bytes);message.setLength(len);//发送sendMessageToService(setter, message);}//关闭inputStream.close();//最后发送一个普通信息包通知用户Message message new Message();message.setMesType(MessageType.MESSAGE_COMM_MES);message.setContent(用户 setter 向你发送了一个文件路径为 path2);message.setGetter(getter);message.setSender(setter);sendMessageToService(setter, message);}//读取文件public void readFile(Message message) throws IOException {String sender message.getSender();String path message.getPath();byte[] bytes message.getBytes();int length message.getLength();//写入到本地路径BufferedOutputStream bufferedOutputStream new BufferedOutputStream(new FileOutputStream(path, true));bufferedOutputStream.write(bytes,0, length);//关闭bufferedOutputStream.close();}5.view/QQView.java更新 6.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段 1.传输文件大小膨胀 一开始由于Message要传输的内容是String类型的所以我就将文件分成很多byte[1024*10]的部分进行传输并且转换成了String但是这个导致了文件变大了很多解决方法在Message中添加属性来保存byte类型的数组和读取到的长度然后再将其放到包中传输在读取的时候以byte数组的形式读取就行 9.功能实现——服务器端推送新闻 1.功能完成 1.qqserver/service/SendAllThread.java package qqserver.service;import qqcommon.Message; import qqcommon.MessageType;import java.io.IOException; import java.io.ObjectOutputStream; import java.util.Collection; import java.util.HashMap; import java.util.Scanner;/*** author 孙显圣* version 1.0* 用来向客户端推送新闻*/ public class SendAllThread extends Thread{Overridepublic void run() {while (true) { //循环获取要推送的信息System.out.println(请输入要推送的消息);Scanner scanner new Scanner(System.in);String content scanner.next();//获取MessageMessage message new Message();message.setMesType(MessageType.MESSAGE_COMM_MES);message.setSender(系统);message.setContent(content);//遍历所有用户并群发HashMapString, ServerConnectClientThread hm ManageClientThreads.getHm();CollectionServerConnectClientThread values hm.values(); //所有的socketfor (ServerConnectClientThread Thread : values) {//获取线程的socket,从而获取对象输出流try {ObjectOutputStream objectOutputStream new ObjectOutputStream(Thread.getSocket().getOutputStream());//输出普通信息包objectOutputStream.writeObject(message);System.out.println(服务器端推送消息 content);} catch (IOException e) {throw new RuntimeException(e);}}}} } 2.qqserver/service/ServerConnectClientThread.java更新 2.调试阶段 1.子线程群发问题 我最初是把Message的内容写好然后调用群发方法发送给各个用户但是我只开了一个用户然后一直测试发现群发不了但是后来想起来我的那个群发方法设置的是不发送给当前的用户真是醉了解决方案自己遍历所有用户群发消息
http://www.zqtcl.cn/news/399456/

相关文章:

  • 湖南专业网站建设服务做网站的底图尺寸多大
  • 山东省住房与建设厅网站首页有名的wordpress主题商
  • 常州市金坛区网站建设毕业设计代做淘宝好还是网站好
  • 品牌网站建设营销型网站设计网站整合方案
  • 网站开发设计师网站代理什么意思
  • 网站层级关系邯郸品牌商标vi设计策划公司
  • 网站开发产品需求说明小网站代码
  • 苏州网站推广排名网站建设方案范文8篇
  • 自己做考试题目网站广州番禺区美食攻略
  • 广州做网站如何如何制作一个网页
  • 网站定制开发收费标准是多少网站代码优化方案
  • 制作卡牌的网站深圳正规煤气公司
  • 手表网站哪家好网站用图片
  • 群辉nas 做网站wordpress linux 中文
  • 平面设计素材网站排名巩义网站建设方案表
  • 延庆网站制作搜索引擎优化的基础是什么
  • 管理手机网站商城网站备案流程
  • 怀化买房网站网站广告js代码添加
  • 做网站 帮别人卖服务器wordpress主题多页面
  • 代理游戏网站潍坊市建设工程管理处网站
  • 大同推广型网站建设网站规划建设与管理维护第二版答案
  • 做网站需要代码吗户外媒体网站建设免费
  • 做什么网站国外做图标网站
  • 网站建设技术部职责门户网站工作总结
  • 用个人电脑做服务器建网站急切网头像在线制作图片
  • 企业网站制作教程浙江省住房和城乡建设厅网站
  • 一个网络空间如何做两个网站哪个网站兼职做设计比较好
  • jquery代码做的网站免费搭建网站模板
  • 铁路建设监理协会官方网站邯郸市网
  • 马鞍山集团网站建设客流分析系统公司