ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

网站通信(二)

2026/8/16 10:01:42 拓冰建站 浏览量
网站通信(二) 文章目录一、Socket 登录注册与数据库1.注册2.登录二、用 Spring Boot 重写 Web 后端三、登录、注册和消息接口四、Java 程序也可以发送 HTTP 请求五、长连接和短连接一、Socket 登录注册与数据库这次开始先把之前聊天室中没有真正完成的数据存储补上之前的账号信息大多保存在ArrayList或者其他内存对象中只要服务器关闭注册过的用户也就没有了。所以登录和注册最终还是需要接到数据库。这次数据库中先建立了一张用户表CREATEDATABASEchatdb;USEchatdb;CREATETABLEusers(idINTAUTO_INCREMENTPRIMARYKEY,usernameVARCHAR(50)UNIQUENOTNULL,passwordVARCHAR(50)NOTNULL,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);Socket 服务端仍然保持原来的结构ServerSocket ↓ accept() ↓ Socket ↓ ClientHandler每有一个客户端连接SocketsocketserverSocket.accept();newClientHandler(socket).start();都会创建一个ClientHandler单独处理这个客户端。这次没有继续使用前面比较复杂的字节协议而是把指令统一写成命令|参数1|参数2例如REGISTER|zhangsan|123456 LOGIN|zhangsan|123456 MSG|lisi|hello QUIT服务端收到后StringcmdLinedis.readUTF();String[]partscmdLine.split(\\|);然后根据第一个字段判断要执行什么功能switch(parts[0].toUpperCase()){caseREGISTER:handleRegister(parts);break;caseLOGIN:handleLogin(parts);break;caseMSG:handleMessage(parts);break;caseFILE:handleFile(parts);break;caseQUIT:logout();return;}这样比前面自己一位一位读取消息类型、用户名长度、消息长度要直观不少。1.注册注册时主要执行数据库插入PreparedStatementstmtconn.prepareStatement(INSERT INTO users(username, password) VALUES (?, ?));stmt.setString(1,user);stmt.setString(2,pass);stmt.executeUpdate();这里使用了PreparedStatement。之前如果直接拼 SQLSELECT * FROM users WHERE usernameusername参数一多比较容易出错使用*?*作为占位符再通过stmt.setString()填入真正的值2.登录登录则查询数据库PreparedStatementstmtconn.prepareStatement(SELECT * FROM users WHERE username? AND password?);如果查询到了记录说明账号密码正确。同时服务端还维护ConcurrentHashMapString,ClientHandleronlineUsers用来保存在线用户用户名 → ClientHandler用户登录成功后ChatServer.onlineUsers.put(user,this);退出时再移除ChatServer.onlineUsers.remove(username,this);这样既能判断一个账号是否已经在线也方便后面根据用户名找到对应连接并转发消息。二、用 Spring Boot 重写 Web 后端Socket 方式能够比较清楚地看到网络通信底层过程但写功能时需要自己处理很多东西。例如前面的程序需要自己处理监听连接 创建线程 读取流 拆消息协议 判断命令 返回结果这次开始使用 Spring Boot。创建项目以后加入了 Web 和 MySQL 等相关依赖然后一个接口可以直接写成RestControllerpublicclassLogin{GetMapping(/login)publicStringlogin(Stringname,Stringpwd){System.out.println(账号name 密码pwd);return登录成功;}}其中RestController表示这个类用来处理Web请求。GetMapping(/login)表示访问/login → 执行login()启动 Spring Boot 后直接访问http://localhost:8080/login?namezhangsanpwd123456Spring Boot 就会自动把name、pwd对应到方法参数。Spring Boot 已经把这些工作处理好了。真正需要自己写的主要变成收到什么参数 ↓ 执行什么业务 ↓ 返回什么结果三、登录、注册和消息接口1.注册接口GetMapping(/register)publicStringregister(Stringname,Stringpwd){for(Useruser:users){if(user.userName.equals(name)){return账号已存在;}}users.add(newUser(name,pwd));return注册成功;}2.登录GetMapping(/login)publicStringlogin(Stringname,Stringpwd){for(Useruser:users){if(user.userName.equals(name)){if(user.password.equals(pwd)){return登录成功;}return密码错误;}}return账号不存在;}还可以直接写GetMapping(/getAll)publicListUsergetAll(){returnusers;}查询所有用户。到了这里服务端功能实际上已经可以拆成多个独立地址/login /register /getAll /sendMsg /getMsg每一个地址负责一件事情。这种写法比所有消息先进同一个 Socket再根据协议自己判断属于哪个功能结构要简单很多。四、Java 程序也可以发送 HTTP 请求Web 服务端并不意味着客户端一定只能是浏览器Java 程序同样可以通过 HTTP 请求 Spring Boot。这次用到了HttpClientHttpRequestHttpResponse例如HttpClientclientHttpClient.newHttpClient();构造请求HttpRequestrequestHttpRequest.newBuilder().uri(URI.create(http://localhost:8080/login?namezhangsanpwd123456)).GET().build();发送HttpResponseStringresponseclient.send(request,HttpResponse.BodyHandlers.ofString());然后System.out.println(response.statusCode());System.out.println(response.body());就可以得到 Spring Boot 返回的结果。所以客户端虽然还是 Java 写的但连接方式已经从newSocket(...)变成了构造 HTTP 请求 ↓ 访问接口 ↓ 得到 HTTP 响应这就引出了这次比较重要的另一个问题Socket 长连接和 HTTP 短连接到底有什么区别五、长连接和短连接之前聊天室使用 Socket客户端一旦连接newSocket(...)连接通常就会一直保留。这种模式可以看成Client Server 一直连接只要连接还在服务端随时可以向客户端发送消息因此聊天的实时性很好。但是问题也很明显**连接本身需要资源。**如果在线用户特别多每一个客户端都需要保持 Socket还可能需要独立线程负责接收消息。假设大部分人都在线服务端就要同时维护大量连接任何一个人发送消息还需要再向其他大量客户端转发。如果简单按照for(...){send(...);}一个个发送人数多以后消息转发本身也会带来明显的压力。HTTP 的工作方式则不一样。一次请求基本是建立连接 ↓ 发送请求 ↓ 服务器处理 ↓ 返回响应 ↓ 请求结束服务端不能像之前的 Socket 一样在任意时间直接沿着原来的连接给客户端发消息所以它更接近一种短连接方式。这次比较下来两种方式各有特点。Socket 长连接优点 实时性高 服务端可以主动推送 适合即时通信 问题 需要一直维护连接 在线用户多时资源压力较大 并发处理更加复杂HTTP 短连接加轮询优点 接口开发简单 连接不需要一直保持 服务端业务结构清楚 问题 不能直接主动推送 需要客户端不断轮询 一定会有延迟