基于JAVA的网络通讯系统设计与实现

news2025/1/26 6:21:21

       一般来说,聊天工具大多数由客户端程序和服务器程序,外加服务器端用于存放客户数据的数据库组成,本系统采用客户机/服务器架构模式,通过Java提供的Socket类来连接客户机和服务器并使客户机和服务器之间相互通信,由于聊天是多点对多点的,而Java提供的多线程功能,用多线程可完成多点对多点的聊天。数据库管理系统用SQL Server2000,完成并通过JDBC-ODBC桥访问数据库。聊天系统完成后将可进行多人对多人的聊天,对好友进行添加、删除,对新用户的注册,发送消息、接受消息,传输文件等功能。界面设计细分化,方便使用者操作和理解。服务器实现了查询和修改等功能,程序逻辑联系较紧密。

3.1 总体构架

基于Java的网络聊天系统设计与实现主要考虑三个个方面的设计,即服务器模块设计,服务器客与客户端通信规则设计和客户端模块设计,本系统整体构架如图3.1所示。

3.2 系统整体功能

基于JAVA的网络聊天系统分为服务器端和客服端,其中:

服务器端包括:在线用户管理模块,用户管理模块,部门管理模块,日志管理模块;

客服端包括:用户认证模块,主界面模块,聊天模块,文件传输模块。

系统整体功能设计如下图2.2所示

3.3 系统模块功能设计

整个系统分为3个模块,其中主要功能如下:

(一)服务器端模块

服务器端模块首先设计多线程来处理客户端的连接,当受到客户端请求的时候,建立一个新的线程来处理客户端的连接。并且在一个注册中心中登记该线程,并存储客户端用户的一些信息,方便服务器统计在线用户,以便与这些在线用户进行通信。除此之外,为了方便用户更好的操作和掌握服务器端,系统还设计四个管理服务器的模块,分别是在线用户管理模块,用户管理模块块,部门管理管理模块和日志管理模块。各个模块功能设计如下:

  1. 在线用户管理模块

该模块主要实现在线用户管理,查看当前登陆账号的所有用户信息(在线用户的)。并且管理员有权限设置用户的在线状态,可以强制用户下线。同时添加一些辅助的功能,比如服务器发送一些系统公告信息等,这样有利于系统消息的及时发送。

  1. 用户管理模块

该模块主要实现新用户的添加、注册用户的删除、修改和查看的功能,管理数据库中存储的用户信息。该模块可以对注册用户的信息(注册用户的编号,姓名,性别,头像,年龄,家庭住址,联系电话,注册时间爱你)进行修改以及查看,并且保证客户端使用这些用户信息登陆服务器。

  1. 部门管理模块

该模块主要实现部门的添加、注册部门的删除、修改和查看的功能,管理数据库中存储的部门信息。该模块可以对注册部门的信息(部门名称,部门描述)进行修改以及查看。

  1. 日志管理模块

日志管理模块主要实现服务器运行状态信息,以及注册用户登陆的一些信息进行记录,管理员可以在需要的时候查看日志,监控服务器的一些状态,以及客户端一些用户的状态。

(二)协议规则模块

该模块主要实现服务器与客户端之间通信规则的制定。其具体思路是:在服务器使用Socket通信的时候,把服务器与客户端通信的信息封装为一个类对象,然后通过自己手动将这些类对象转化为输入流,并在另一端输出流,根据自己制定的规则逆序解析流,把流转化为类对象。实现服务器与客户端之间的通信。其中具体的表示信息要自己制定,比如登陆成功标示,失败标示,修改密码标示,聊天标示等,这些都需要自己通过一些数字标记。

(三)客户端模块

客户端模块主要实现,用户账号到服务器的认证,以及登陆之后与其他用户通信或者部门通信,除了简单文本聊天之外,也考虑用户之间文件传输的功能。其中客户端之间的通信,是在客户端之间搭建服务器,也即客户端自己建立SocketServer,并用多线程来处理对不同用户的聊天。用户之间文件传输也是通过搭建客户端之间的服务器来实现文件出传的,不需要通过服务器来实现信息的传输,可以减少延迟,也减少了服务器端的流量损失。但是在客户之间搭建连接的时候仍然需要通过服务器来实现一些简单的通知操作,实现客户端与客户端之间搭建其连接。因此客户端大体分四个模块,分别是用户认证模块,主界面模块,聊天模块和文件传输模块。各个功能模块设计如下:

  1. 用户认证模块

可以模仿QQ登陆界面,输入用户名和密码,以及服务器IP和端口,实现账号和密码到服务器的认证,判断用户是否有权限登录主界面进行信息沟通。

  1. 主界面模块

该模块主要实现类似QQ友好的界面,查看自己的信息,修改密码,显示好友列表组列表信息。使用Swing现有组建扩展制作合适的界面设计,并搭配后台功能。

  1. 聊天模块

该模块主要实现不同用户之间信息的发送,如何实现点对点用户通信,多点用户通信,要考虑多线程的设计才可以实现。同时信息发送除了文本之外还要考虑其他信息格式比如图片之类。

  1. 文件传输模块

实现不同用户之间多线程的文件传输。

4.1 系统E-R图

(1) E-R模型

基于网络聊天系统数据库E-R模型,如图 所示:

4.2 数据字典

(1) 用户表  (用户ID(主键),用户名,密码,用户密码,性别,头像ID,部门ID,年龄,电话号码,地址,注册时间,状态)。

(2) 分组信息表(分组ID(主键),分组名字,标记)。

4.3 各个数据表的创建

基于网络聊天系统数据库包括部门信息以及用户信息,部门信息表格和用户信息表格的设计结果如表4.1,表4.2所示:每个表格表示在数据库中的一个表。

表4.1部门信息表(T_DEPARTMENT)

列名

数据类型

可否为空

字段名称

字段大小

D_ID

VARCHAR

No null

分组ID

3

D_NAME

VARCHAR

  分组名字

50

D_REMARK

VARCHAR

标记

100

表4.2   用户表(T_USER)

列名

数据类型

可否为空

字段名称

字段大小

U_ID

VARCHAR

Not  null

用户ID

6

U_NAME

VARCHAR

用户名

50

U_PASSWORD

VARCHAR

用户密码

20

U_SEX

VARCHAR

性别

3

U_ICONID

INT

头像ID

4

U_DEPTID

VARCHAR

部门ID

3

U_AGE

INT

年龄

4

U_TEL

VARCHAR

电话号码

20

U_ADDRESS

VARCHAR

地址

100

U_REGTIME

DATETIME

注册时间

8

U_ISONLINE

INT

状态

4

4.4 数据库的连接

打开控制面板,找到管理工具,在选中数据源配置,进行ODBC数据源的配置其中主要的配置流程如下面图所示:

 

 

5.1 服务器功能模块的设计

5.1.1 Socket和ServerSocket介绍

Socket,简称套接字,用于实现网络上客户和服务器之间的连接。也就是说网络上两个或两个以上双工方式通信的进程之间总有一个连接,这个连接的端点成为套接字,套接字是在比较低的层次上通信的。

具体的说:一个服务器应用程序一般侦听一个特定的端口等待客户端的连接请求,当一个连接请求到达时,客户端和服武器端建立一个通信连接,在连接过程中,客户端被分配一个本地端口与一个socket建立连接,客户端通过写socket来通知服务器,以读socket中的信息,类似的服务器也获得一个本地端口,它需要一个新的端口号来侦听原始端口上的其他连接请求。服务器也通过它的本地端口连接一个socket,通过读写和客户端通信。

Socket程序的工作过程:

1、建立Socket连接:在通信开始之前由通信双方确认身份,建立一条专用的虚拟连接通道。

2、数据通信:利用虚拟连接通道传送数据信息进行通道。

3、关闭:通信结束时,再将所建的虚拟连接拆除。

实现套接字的服务端,需要使用ServerSocket 类。ServerSocket类是服务器程序的运行基础,它允许程序绑定一个端口号来监听客户端的请求,一旦产生客户端请求,它将接受这一请求,同时产生一个完整的Socket 连接对象。服务器绑定的端口必须公开,以便让客户端程序知道如何连接这个服务器。同时,作为服务器,它必须能够接收多个客户的请求,这就需要为服务器设置一个请求队列,如果服务器不能马上响应客户端的请求,要将这个请求放进请求队列中,等服务器将当前的请求处理完,会自动到请求队列中按照先后顺序取出请求进行处理。服务器的资源是有限的,这就导致它的最大连接数是有限的,通过ServerSocket 的构造函数可以指定这个最大连接数。如果不明

确指定这个连接数,默认最大连接数为50,也就是说,客户端的请求队列最大可以容纳50 个请求,当超过这个最大连接数时,用户的请求将不再会被响应。 利用SocketServer 也提供了一些方法,它们主要有:

accept()             返回一个“已连接”的Socket 对象

getInetAddress()     得到该服务器的IP 地址

getLocalPort()       得到服务器所侦听的端口号

setSoTimeout()      设置服务器超时时间

getSoTimeout()      得到服务器超时时间

服务器和客户端通过Socket简单通信框架下图所示。

 

5.2 服务器功能模块的实现

5.2.1 多线程服务器模块实现

服务器模块核心功能是使用Socket Server实现多线程的服务器,针对每一个客户建立一个单独的线程处理客户端的请求。其具体思路是,首先建立Socket Server,并绑定服务器IP和某个未使用的端口。然后监听该端口,如果有客户端的Socket连接则建立一个客户端线程ClientThread类对象,有该对象处理该客户的一些请求,并在ClientThread中保存User Bean对象,该对象保存了用户的一些基本信息,比如账号、密码、年龄、是否在线等。如果服务器没有关闭Socket Server则服务器则一直处理客户端连接。其核心代码如下。

public class MessageServer extends Thread {

     //服务器端SocketServer

       private ServerSocket server;

       private boolean isStop = false;

       public MessageServer(int port) throws IOException {

              server = new ServerSocket(port);

       }

       /**

        * 消息服务

        */

       public void run() {

              Socket client;

              try {

                     while (!isStop) {

                 //建立处理客户端连接的线程

                            client = server.accept();

                            (new ClientThread(client)).start();

                     }

              } catch (IOException e) {

                      e.printStackTrace();

              } finally {

                     if (server != null) {

                            try {

                                   server.close();

                            } catch (IOException e1) {

                                   // e1.printStackTrace();

                            }

                     }

                     isStop = true;

              }

       }

}

5.2.2 在线用户管理模块实现

该模块主要实现在线用户统计、多线程服务器的启动关闭、以及服务器发送系统公告等功能。其中在线用户统计是在客户端每次连接的时候服务器建立了ClientThread来单独处理某个客户请求,如果该客户登陆成功的话则在工具类PubValue中一个HashMap添加该用户信息,查看在线用户信息的时候,可以通过工具类PubValue来的HashMap读取在线用户列表。系统公告则是通过服务器根据在线用户列表,广播系统公告到每个客户端,实现信息的公布。其实核心代码如下:

private void login(Message message) throws IOException {

              if (message.getType().equals(PackOper.LOGIN)) {

                     User user = (User) message;        

                     List list = UserDaoFactory.getUserDao().selectId(user.getId());

                     if (list.size() > 0) {

                            User tempUser = (User) list.get(0);                          

                            if (tempUser.getPassword().equals(user.getPassword())) {

                                   if (tempUser.getIsOnline() == Parameter.ONLINE) {                                                   message.setType(PackOper.LOGIN_ONLINED);

                                          myObjectOut.writeMessage(message);

                                          this.setStop();

                                          return;

                                   }

                                   ServerUI.getInstance().getLogUI().addLog(

                                                 LogOper.getInstance().insertOnLineLog(tempUser));

                                   //登录成功后

                                   //先通知所有人

                                   //再通知此用户

                                   //再把自己加到线程组中

                                   //接着更新数据库

                                   //下载树给此用户

                                   tempUser.setType(PackOper.LOGIN_SUCCEED);

                                   tempUser.setIsOnline(pub.Parameter.ONLINE);

                                   this.chatCompany(tempUser);

                                   this.user = tempUser;

                                   this.myObjectOut.writeMessage(tempUser);

                                   PubValue.addUserThread(this.user.getId(), this);

                                   UserDaoFactory.getUserDao().setOnline(tempUser.getId(),

                                                 Parameter.ONLINE);

                                   ServerUI.getInstance().getOnLineUI().updateOnLine();

                                   try {

                                          Thread.sleep(10);

                                   } catch (InterruptedException e1) {

                                          // e1.printStackTrace();

                                   }

                                   // 登录成功后发送树

                                   PubValue.company.setType(PackOper.ADD_COMPANY);

                                   myObjectOut.writeMessage(PubValue.company);

                            list = DepartmentDaoFactory.

getDepartmentDao().select(null);

                                   Iterator it = list.iterator();

                                   while (it.hasNext()) {

                                          message = (Message) it.next();

                                          message.setType(PackOper.ADD_DEPARTMENT);

                                          myObjectOut.writeMessage(message);

                                   }

                                   list = UserDaoFactory.getUserDao().select(null);

                                   it = list.iterator();

                                   while (it.hasNext()) {

                                          message = (Message) it.next();

                                          message.setType(PackOper.ADD_USER);

                                          myObjectOut.writeMessage(message);

                                   }

                                   return;

                            }

                     }

                     message.setType(PackOper.LOGIN_DEFEATED);

                     myObjectOut.writeMessage(message);

                     this.setStop();

              }

       }

    其实现效果如图所示:

 

5.2.3 部门管理模块实现

该模块主要实现对部门进行管理,根据情况添加,删除或者修改部门等。其设计主要是通过SQL语句来操作数据库数据的。跟普通的管理系统设计思路一样。没有太多的算法,主要是SQL语句的构造,以及数据库操作的API函数的使用。其实现效果如图所示.

 

5.2.4 用户管理模块实现

       该模块类似于部门管理模块的设计,主要是针对用户的一些信息进行管理,根据需要添加、删除或者修改用户信息。其实现主要也是SQL语句的构造以及JDBC驱动提供函数的使用。比较复杂的部分设计主要体现界面的设计,如何使用Swing构造友好并且易于交互的界面需要花费一些时间。以及实现组合条件的用户查询,可以根据用户账号查询,或者所在部门查询,或者编号查询,或者根据这三个条件组合查询,这一功能比较复杂,如何根据不同的情况构造合适的SQL语句进行数据查询,显得很重要,否则粗略的设计会增加很多冗余代码。其整体实现效果如下图5.4所示。

 

5.2.5日志管理模块实现

日志管理模块主要是监控服务器的一些状态,以及客户端用户登陆的情况。这些日志信息记录是通过工具类LogOper实现日志的记录。该类使用了设计模式中单例模式,保证服务器端在进行日志操作的时候只有一个实例在内存中进行日志的记录。其日志的存放路径主要是在系统目录下,其文件名为server.log。通过服务器UI界面查询日志信息也是通过读取该文件来查询日志记录的。其实现效果如下图5.5:

 

5.3协议设计与实现

5.3.1 协议规则

协议规则里主要用一些整数来表示服务器与客户端通信的时候包的类型。服务器可以按照这个规则来解析包,并根据包中的信息做出相应的操作。其标示的具体含义和定义规则下表5.1所示。

表5.1  协议规则表

标示名字

标示值

标示意义

LOGIN

10

登录包标识

LOGIN_SUCCEED

11

登录成功标识

LOGIN_DEFEATED

12

登录失败标识

LOGIN_ONLINED

13

已经在线标识

ADD_COMPANY

14

添加公司标识

ADD_DEPARTMENT

15

添加部门标识

ADD_USER

16

添加用户包标识

UPDATE_COMPANY

17

更新公司信息标识

UPDATE_DEPARTMENT

18

更新部门信息

UPDATE_USER

19

更新用户信息

DELETE_DEPARTMENT

20

删除部门

DELETE_USER

21

删除用户

CHAT_USER

22

私聊

CHAT_DEPARTMENT

23

部门聊天

CHAT_ALL

24

公司聊天

MESSAGE

25

公司通知

UPFILE

26

要求传送文件

UPFILE_FIAT

27

许可传送文件

SEND_DEFUSE

28

发送方取消

UPFILE_DEFUSE

29

不许可传送文件

DOWN_LINE

30

下线包

FORCEDOWN_LINE

31

强制下线包

UPPASSWORD

32

修改密码

UPPASSWORD_DEFEATED

33

原密码不对

UPPASSWORD_NEW_NULL

34

新密码为空

UPPASSWORD_SUCCEED

35

修改密码成功

SERVERCLOSE

36

服务器关闭

5.3.2 协议实现

协议的指定主要是考虑在进行通信的过程中,保证服务器和客户端的通信消息能够被双方正确的理解,并作出相应的处理,比如登陆失败消息,登陆成功消息,文件传输消息,聊天消息等,服务器和客户端必须要按照指定的规则去打包和解析消息。其中协议的设计是通过对Socket流进行自定义的封装,把已经定义好的对象的每一个属性按照unicode编码中的一些特殊的编码\u0000(空格)进行分隔封装,并发送输入和输出流,另一端安装封装的逆序把输出流封装为对象,并识别该对象随对应是什么样的消息,客户端或者服务器作出相应的处理。而通过继承输入输出流实现了服务器器信息的发送,但是具体的包类型是什么样的消息是通过标示来标记。通过标示来解析这些包是什么样类型的包,实现服务器和客户端之间可理解的通信。其核心类PackOper类主要代码如下:

/**

 * 解包类 

 * @author Administrator

 */

public class PackOper implements PackInterface {/**

        * 创建一个包,根据object类型

        * @param object

        * @return

        */

       public byte[] createPackage(Object object) {

              if (object == null) {

                     return null;

              }

              if (object instanceof MessagePack) {

                     return createMessagePack((MessagePack) object);

              }

              if (object instanceof User) {

                     return createUser((User) object);

              }

              if (object instanceof Department) {

                     return createDepartment((Department) object);

              }

              if (object instanceof Company) {

                     return createCompany((Company) object);

              }

              return null;

       }

    /**

        * 创建消息包

        * @param pack

        * @return

        */

       private byte[] createMessagePack(MessagePack pack) {

              return createByte(STARTSEPARATOR + pack.getType() + SEAS

                            + pack.getFrom() + SEAS + pack.getFromIP() + SEAS

                            + pack.getFromPort() + SEAS + pack.getTo() + SEAS

                            + pack.getMessage() + ENDSEPARATOR);

       }

    /**

        * 解一般消息包

        * @param strs

        * @return

        */

       private Message unbindMessagePack(String[] strs) {

              MessagePack messagePack = null;

              if (strs != null) {

                     try {

                            messagePack = new MessagePack();

                            messagePack.setType(strs[0]);

                            messagePack.setFrom(strs[1]);

                            messagePack.setFromIP(strs[2]);

                            messagePack.setFromPort(Integer.parseInt(strs[3]));

                            messagePack.setTo(strs[4]);

                            messagePack.setMessage(strs[5]);

                     } catch (Exception e) {

                            return null;

                     }

              }

              return

       } 

}

    MyObjectInputStream.java,该文件继承输出流,并自定义解析输出流的规则。Unit包里封装了所有消息类封装Bean。MyObjectInputStream.java,该文件继承输入流,并自定义解析输入流的规则。PackOper.java该文件主要实现了,Socket发送字节流的解包规则,并转化字节流为对象。也即服务器实现客户端不同消息识别,服务器端并作出相应处理。

5.4客户端功能模块的设计与实现

5.4.1 登陆认证模块

该模块主要实现用户的登陆认证,以及服务器代理IP和端口的设置,并检查数据的合法性。主要是参考现在通用即时聊天工具QQ界面进行设计,没有采用具体的算法实现漂亮的算法,只是截取QQ界面的图片作为自己设计界面的背景。同时用户登录身份认证使用简单模拟的QQ协议通过发送登陆验证包消息给服务器,服务器解包之后查询数据库检查用户身份的合法性,并发送给客户端认证成功或者失败包,客户端解包之后如果认证成功则登陆成功,显示主界面,否则提示用户登陆失败,提示用户重新登陆。并且可以根据服务器IP和端口的变化进行动态设置服务器地址,具有很好的灵活性。具体实现效果如下图5.6所示。

 

未完待续。。。 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/433676.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTB-Cascade

HTB-Cascade 信息收集立足s.smith -> arksvc使用脚本获取CascAudit.exe加密的密码明文修改IL指令获取 arksvc -> administrator 信息收集 查看smbclient。 rpcclient空密码连接并收集信息。 收集到用户列表,此外没有有意思的信息。 CascGuest arksvc s.smi…

Qt/QML编程学习之心得:二进制兼容、私有实现及Q_D/Q_Q指针(三)

QML画好或说描述好界面之后,实现部分通过C++实现,Qt采用了私有实现的设计模式解决了二进制兼容的问题。 二进制兼容问题描述: 现在有一个Widget, 包含一个私有成员变量m_geometry ,编译 Widget 并且发布为MyWidgetLib 1.0 。该应用程序名字为TestAPP,基于 Qt 4.9。 cla…

【Linux网络服务】Linux网络设置

一、查看网络配置 1.1ifconfig 1.2ip a 1.3什么是mtu 最大传输单元MTU,是指网络能够传输的最大数据包大小,以字节为单位。MTU的大小决定了发送端一次能够发送报文的最大字节数。如果MTU超过了接收端所能够承受的最大值,或者是超过了发送路径…

异步爬虫的原理和解析

我们知道爬虫是 IO 密集型任务,比如如果我们使用 requests 库来爬取某个站点的话,发出一个请求之后,程序必须要等待网站返回响应之后才能接着运行,而在等待响应的过程中,整个爬虫程序是一直在等待的,实际上…

焦虑症会出现哪些问题 什么因素导致的焦虑症

当说起焦虑症,大多数人想到的就是植物神经紊乱,确实,这两种疾病是非常容易混淆的,甚至很多时候植物神经紊乱都会当做焦虑症进行治疗,虽然这种疾病大多效果不会太理想。 你们知道什么是焦虑症吗? 很多人当出…

Android ProtoLog动态开启相关wm logging源码分析补充

Android ProtoLog动态开启相关wm logging源码分析补充 针对上一节已经清楚了相关的代码中怎么可以打印到logcat中,其实本质上还就是protologtool这个工具对代码中的所有ProtoLog进行了相关的替换成了具体实现,最后会条件判断输出到Slog中 本文就重点来看…

【池化方法】多示例学习池化(MIL pooling)公式与代码

一般的池化方法包括最大池化、平均池化、自适应池化与随机池化,这几天意外看到了多示例学习池化,感觉挺有意思的,记录一下。   论文   代码 1. 多示例学习(Multiple instance learning,MIL) 经典深度学…

梯度下降算法原理详解及MATLAB程序代码(最简单)

模型就是线性规划及线性规划的对偶理论,单纯形法以及它的实际应用:整数规划及其解法(分支定界法、割平面法匈牙利算法Q),目标规划,非线性规划动态规划、决策分析等等。 其它的一些优化算法。比如说一维搜索里面的黄金分割法、加步…

PostMan笔记(二)发送请求

1. 发送请求功能介绍 Postman是一款流行的API开发工具,它可以让开发人员更方便地测试、调试和使用API。其中,发送请求功能是Postman最为重要和基础的功能之一。 在Postman中,发送请求功能主要包括以下几个步骤: 选择请求方法&am…

数据分析时,进行数据建模该如何筛选关键特征?

1.为什么要做关键特征筛选? 在数据量与日俱增的时代,我们收集到的数据越来越多,能运用到数据分析挖掘的数据也逐渐丰富起来,但同时,我们也面临着如何从庞大的数据中筛选出与我们业务息息相关的数据。(大背景…

Java的对象克隆

本节我们会讨论 Cloneable 接口,这个接口指示一个类提供了一个安全的 clone() 方法。 Object 类提供的 clone() 方法是 “浅拷贝”,并没有克隆对象中引用的其他对象,原对象和克隆的对象仍然会共享一些信息。深拷贝指的是:在对象中…

微服务---一篇学完SpringCloud

SpringCloud 1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢? 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff1a…

java企业级信息系统开发学习笔记06基于xml配置方式使用Spring MVC

文章目录 一、学习目标二、Spring MVC概述1、MVC架构2、Spirng MVC3、使用Spring MVC的两种方式 三、基于xml配置与注解的方式使用Spring MVC(一)创建Maven项目(二)添加相关依赖(三)给项目添加Web功能&…

SpringMVC表格提交中文乱码和配置logback

最佳解决方案还是使用Spring提供的过滤器&#xff0c;将其配置到WEB.XML文件中&#xff1a; <filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class&g…

nginx部署VUE项目

前言 目前公司的前端代码基本都是部署在nginx下&#xff0c;特此来记录一下 开发环境&#xff1a;window10 nginx环境搭建&#xff08;参考下方文章&#xff09; window环境安装 mac环境安装 本地我将nginx放置于F盘 前端项目打包 一个nginx服务下可能会放置多个前端包&…

echarts 折线图

Echarts 常用各类图表模板配置 注意&#xff1a; 这里主要就是基于各类图表&#xff0c;更多的使用 Echarts 的各类配置项&#xff1b; 以下代码都可以复制到 Echarts 官网&#xff0c;直接预览&#xff1b; 图标模板目录Echarts 常用各类图表模板配置一、简洁折线图二、环形图…

结构体的存储

由于要想知道结构体的大小&#xff0c;了解结构体是如何存储在内存中的 我们需要先了解一个知识点&#xff1a; 结构体内存对齐 1. 第一个成员在与结构体变量偏移量为0的地址处 (偏移量是某个字节相较于起始存储空间的相差字节数 例如第一个字节的偏移量是0&#xff0c;第二个…

一套专业的C#医院体检管理系统源码 PEIS体检报告管理系统源码 C/S医院PEIS系统源码

医院PEIS体检管理系统源码&#xff0c;有源码&#xff0c;有演示&#xff0c;自主研发&#xff0c;官方正版授权&#xff01; 开发语言&#xff1a;C# 开发工具&#xff1a;VS2013版本起 后端框架&#xff1a;winform 数 据 库&#xff1a;oracle 12c 医院体检系统主要特点…

人大金仓亮相2023CHITEC,五大看点不容错过

近日&#xff0c;由中国卫生信息与健康医疗大数据学会和《中国卫生信息管理杂志》社联合举办的2023&#xff08;17th&#xff09;中国卫生信息技术/健康医疗大数据应用交流大会暨软硬件与健康医疗产品展览会&#xff08;2023 CHITEC&#xff09;在安徽合肥顺利召开。 作为数据库…

【DAY38】BOM/VUE初步学习

pageXOffset 设置或返回当前页面相对于窗口显示区左上角的 X 位置。 pageYOffset 设置或返回当前页面相对于窗口显示区左上角的 Y 位置。 screenLeft&#xff0c;screenTop&#xff0c;screenX&#xff0c;screenY 声明了窗口的左上角在屏幕上的的 x 坐标和 y 坐标。IE、Safari…