javasocket实现服务端,客户端简单网络通信。chat

裕轻松哈哈      2022-05-04     482

关键词:

之前写的实现简单网络通信的代码,有一些严重bug。后面详细写。

根据上次的代码,主要增加了用户注册,登录页面,以及实现了实时显示当前在登录状态的人数。并解决一些上次未发现的bug。(主要功能代码参见之前随笔 https://www.cnblogs.com/yuqingsong-cheng/p/12740307.html)

 

实现用户注册登录就需要用到数据库,因为我主要在学Sql Server。Sql Server也已支持Linux系统。便先在我的电脑Ubuntu系统下进行安装配置。

链接:https://docs.microsoft.com/zh-cn/sql/linux/quickstart-install-connect-red-hat?view=sql-server-ver15     

Sql Server官网有各个系统的安装指导文档,所以按照正常的安装步骤,一切正常安装。

可放到服务器中却出现了问题。阿里云学生服务器是2G内存的(做活动外加学生证,真的很香。但内存有点小了)。sqlserer需要至少2G内存。所以只能放弃SqlServer,转向Mysql。

同样根据MySql的官方指导文档进行安装。但进行远程连接却需要一些“乱七八糟”的配置,于是开始“面向百度连接”,推荐一个解决方案,https://blog.csdn.net/ethan__xu/article/details/89320614     适用于mysql8.0以上版本。

 

数据库部分解决,开始写关于登录,注册类。登录注册部分新开了一个端口进行socket连接。由于功能较简单,所以只用到了插入,查询语句。

客户端读入用户输入的登录,注册信息,发送至服务端,服务端在连接数据库进行查询/插入操作,将结果发送至客户端。

实例代码

  1 package logindata;
  2 
  3 import java.io.DataInputStream;
  4 import java.io.DataOutputStream;
  5 import java.io.IOException;
  6 import java.net.ServerSocket;
  7 import java.net.Socket;
  8 import java.sql.Connection;
  9 import java.sql.DriverManager;
 10 import java.sql.ResultSet;
 11 import java.sql.SQLException;
 12 import java.sql.Statement;
 13 import java.util.ArrayList;
 14 
 15 public class LoginData implements Runnable{
 16 
 17     static ArrayList<Socket> loginsocket = new ArrayList();
 18     
 19     public LoginData() { }
 20 
 21     @Override
 22     public void run() {
 23         ServerSocket serverSocket=null;
 24         try {
 25             serverSocket = new ServerSocket(6567);
 26         } catch (IOException e) {
 27             e.printStackTrace();
 28         }
 29         while(true) {
 30             Socket socket=null;
 31             try {
 32                 socket = serverSocket.accept();
 33             } catch (IOException e) {
 34                 // TODO Auto-generated catch block
 35                 e.printStackTrace();
 36             }
 37             loginsocket.add(socket);
 38             
 39             Runnable runnable;
 40             try {
 41                 runnable = new LoginDataIO(socket);
 42                 Thread thread = new Thread(runnable);
 43                 thread.start();
 44             } catch (IOException e) {
 45                 // TODO Auto-generated catch block
 46                 e.printStackTrace();
 47             }
 48         }
 49     }
 50 }
 51 
 52 class LoginDataIO implements Runnable{
 53 
 54     String b="false";
 55     Socket socket;
 56     DataInputStream inputStream;
 57     DataOutputStream outputStream;
 58     public LoginDataIO(Socket soc) throws IOException {
 59         socket = soc;
 60         inputStream = new DataInputStream(socket.getInputStream());
 61         outputStream = new DataOutputStream(socket.getOutputStream());
 62     }
 63     
 64     @Override
 65     public void run() {
 66         String readUTF = null;
 67         String readUTF2 = null;
 68         String readUTF3 = null;
 69         try {
 70             readUTF = inputStream.readUTF();
 71             readUTF2 = inputStream.readUTF();
 72             readUTF3 = inputStream.readUTF();
 73         } catch (IOException e) {
 74             e.printStackTrace();
 75         }
 76         
 77 //        System.out.println(readUTF+readUTF2+readUTF3);
 78         
 79         SqlServerCon serverCon = new SqlServerCon();
 80         try {
 81             //判断连接是登录还是注册,返回值不同。
 82             if(readUTF3.equals("login")) {
 83                 b=serverCon.con(readUTF, readUTF2);
 84                 outputStream.writeUTF(b);
 85             }else {
 86                 String re=serverCon.insert(readUTF, readUTF2);    
 87                 outputStream.writeUTF(re);
 88             }
 89         } catch (SQLException e) {
 90             // TODO Auto-generated catch block
 91             e.printStackTrace();
 92         } catch (IOException e) {
 93             // TODO Auto-generated catch block
 94             e.printStackTrace();
 95         } catch (ClassNotFoundException e) {
 96             // TODO Auto-generated catch block
 97             e.printStackTrace();
 98         }  
 99         
100 //        System.out.println(b);
101     }
102 }
103 
104 
105 class SqlServerCon {
106 
107     public SqlServerCon() {
108         // TODO Auto-generated constructor stub
109     }
110     
111     String name;
112     String password;
113 //    boolean duge = false;
114     String duge = "false";
115 //    String url = "jdbc:sqlserver://127.0.0.1:1433;"
116 //            + "databaseName=TestData;user=sa;password=123456";
117     /**
118      * com.mysql.jdbc.Driver 更换为 com.mysql.cj.jdbc.Driver。
119         MySQL 8.0 以上版本不需要建立 SSL 连接的,需要显示关闭。
120         最后还需要设置 CST。
121      */
122     //连接MySql数据库url格式
123     String url = "jdbc:mysql://127.0.0.1:3306/mytestdata?useSSL=false&serverTimezone=UTC";
124     public String con(String n,String p) throws SQLException, ClassNotFoundException {
125         Class.forName("com.mysql.cj.jdbc.Driver");
126         Connection connection = DriverManager.getConnection(url,"root","uu-7w3yfu?VX");
127 //        System.out.println(connection);
128         
129         Statement statement = connection.createStatement();
130 //        statement.executeUpdate("insert into Data values('china','123456')");
131         ResultSet executeQuery = statement.executeQuery("select * from persondata");
132         
133         //登录昵称密码确认
134         while(executeQuery.next()) {
135             name=executeQuery.getString(1).trim();
136             password = executeQuery.getString(2).trim();   //"使用这个方法很重要"  String     trim()      返回值是此字符串的字符串,其中已删除所有前导和尾随空格。
137 //            System.out.println(n.equals(name));
138             if(name.equals(n) && password.equals(p)) {
139                 duge="true";
140                 break;
141             }
142         }
143         statement.close();
144         connection.close();
145 //        System.out.println(duge);
146         return duge;
147     }
148     
149     public String insert(String n,String p) throws SQLException, ClassNotFoundException {
150         boolean b = true;
151         String re = null;
152         Class.forName("com.mysql.cj.jdbc.Driver");
153         Connection connection = DriverManager.getConnection(url,"root","uu-7w3yfu?VX");
154         Statement statement = connection.createStatement();
155         
156         ResultSet executeQuery = statement.executeQuery("select * from persondata");
157         while(executeQuery.next()) {
158             name=executeQuery.getString(1).trim();
159 //            password = executeQuery.getString(2).trim();  
160             if(name.equals(n)) {
161                 b=false;
162                 break;
163             }
164         }
165         
166         //返回登录信息
167         if(b && n.length()!=0 && p.length()!=0) {
168             String in = "insert into persondata "+"values("+"'"+n+"'"+","+"'"+p+"'"+")";  //这条插入语句写的很捞,但没想到更好的。
169 //            System.out.println(in);
170             statement.executeUpdate(in);
171             statement.close();
172             connection.close();
173             re="注册成功,请返回登录";
174             return re;
175         }else if(n.length()==0 || p.length()==0 ) {
176             re="昵称或密码不能为空,请重新输入";
177             return re;
178         }else {
179             re="已存在该昵称用户,请重新输入或登录";
180             return re;
181         }
182     }
183 }

 

因为服务端需要放到服务器中,所以就删去了服务端的用户界面。

 1 import file.File;
 2 import logindata.LoginData;
 3 import server.Server;
 4 
 5 public class ServerStart_View {
 6     
 7     private static Server server = new Server();
 8     private static File file = new File();
 9     private static LoginData loginData = new LoginData();
10     public static void main(String [] args) {
11         ServerStart_View frame = new ServerStart_View();
12         server.get(frame);
13         Thread thread = new Thread(server);
14         thread.start();
15         
16         Thread thread2 = new Thread(file);
17         thread2.start();
18         
19         Thread thread3 = new Thread(loginData);
20         thread3.start();
21     }
22     public void setText(String AllName,String string) {
23         System.out.println(AllName+" : "+string);
24     }
25 }

 

客户端,登录界面与服务带进行socket连接,发送用户信息,并读取返回的信息。

主要代码:

 1 public class Login_View extends JFrame {
 2 
 3     public static String AllName=null;
 4     static Login_View frame;
 5     private JPanel contentPane;
 6     private JTextField textField;
 7     private JTextField textField_1;
 8     JOptionPane optionPane = new JOptionPane();
 9     private final Action action = new SwingAction();
10     private JButton btnNewButton_1;
11     private final Action action_1 = new SwingAction_1();
12     private JLabel lblNewLabel_2;
13 
14     /**
15      * Launch the application.
16      */
17     public static void main(String[] args) {
18         EventQueue.invokeLater(new Runnable() {
19             public void run() {
20                 try {
21                     frame = new Login_View();
22                     frame.setVisible(true);
23                     frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
24                 } catch (Exception e) {
25                     e.printStackTrace();
26                 }
27             }
28         });
29     }
30 
31 ..................
32 ..................
33 ..................
34 
35 private class SwingAction extends AbstractAction {
36         public SwingAction() {
37             putValue(NAME, "登录");
38             putValue(SHORT_DESCRIPTION, "点击登录");
39         }
40         public void actionPerformed(ActionEvent e) {
41             String text = textField.getText();
42             String text2 = textField_1.getText();
43 //            System.out.println(text+text2);
44 //            boolean boo=false;
45             String boo=null;
46             try {
47                 boo = DataJudge.Judge(6567,text,text2,"login");
48             } catch (IOException e1) {
49                 e1.printStackTrace();
50             }
51             if(boo.equals("true")) {
52                 ClientStart_View.main1();
53                 AllName = text;    //保存用户名
54                 frame.dispose();    //void    dispose()    释放此this Window,其子组件和所有其拥有的子级使用的所有本机屏幕资源 。
55             }else {
56                 optionPane.showConfirmDialog
57                 (contentPane, "用户名或密码错误,请再次输入", "登录失败",JOptionPane.OK_CANCEL_OPTION);
58             }
59         }
60     }
61     
62     private class SwingAction_1 extends AbstractAction {
63         public SwingAction_1() {
64             putValue(NAME, "注册");
65             putValue(SHORT_DESCRIPTION, "点击进入注册页面");
66         }
67         public void actionPerformed(ActionEvent e) {
68             Registered_View registered = new Registered_View(Login_View.this);
69             registered.setLocationRelativeTo(rootPane);
70             registered.setVisible(true);
71         }
72     }
73 }

连接服务端:第一次写的时候连接方法是Boolean类型,但只适用于登录的信息判断,当注册时需要判断昵称是否重复,密码昵称是否为空等不同的返回信息,(服务端代码有相应的判断字符串返回,参上)于是该为将连接方法改为String类型。

 1 import java.io.DataInputStream;
 2 import java.io.DataOutputStream;
 3 import java.io.IOException;
 4 import java.net.Socket;
 5 import java.net.UnknownHostException;
 6 
 7 public class DataJudge {
 8 
 9     /*public static boolean Judge(int port,String name,String password,String judge) throws UnknownHostException, IOException {
10         
11         Socket socket = new Socket("127.0.0.1", port);
12         DataInputStream inputStream = new DataInputStream(socket.getInputStream());
13         DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
14         
15         outputStream.writeUTF(name);
16         outputStream.writeUTF(password);
17         outputStream.writeUTF(judge);
18         
19         boolean readBoolean = inputStream.readBoolean();
20         
21         outputStream.close();
22         inputStream.close();
23         socket.close();
24         return readBoolean;
25     }*/
26 
27 public static String Judge(int port,String name,String password,String judge) throws UnknownHostException, IOException {
28     
29         //连接服务端数据库部分
30         Socket socket = new Socket("127.0.0.1", port);
31         DataInputStream inputStream = new DataInputStream(socket.getInputStream());
32         DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
33         
34         outputStream.writeUTF(name);
35         outputStream.writeUTF(password);
36         outputStream.writeUTF(judge);
37         
38         String read = inputStream.readUTF();
39         
40         //登录是一次性的,所以要及时关闭socket
41         outputStream.close();
42         inputStream.close();
43         socket.close();
44         return read;
45     }
46 }

 

用户注册界面,主要代码:

 1 public class Registered_View extends JDialog{
 2 //    DataJudge dataJudge = new DataJudge();
 3     private JTextField textField_1;
 4     private JTextField textField;
 5     JLabel lblNewLabel_2;
 6     private final Action action = new SwingAction();
 7     
 8     public Registered_View(JFrame frame) {
 9         super(frame, "", true);   //使注册对话框显示在主面板之上。
10                 .........
11                 .........
12                 .........
13                 .........
14         }  
15       
16         private class SwingAction extends AbstractAction {
17         public SwingAction() {
18             putValue(NAME, "注册");
19             putValue(SHORT_DESCRIPTION, "点击按钮进行注册");
20         }
21         public void actionPerformed(ActionEvent e) {
22             String b=null;  //用于接收服务端返回的注册信息字符串
23             String name = textField.getText();
24             String password = textField_1.getText();
25             try {
26                 b = DataJudge.Judge(6567, name, password, "registered");
27             } catch (IOException e1) {
28                 // TODO Auto-generated catch block
29                 e1.printStackTrace();
30             }
31             
32             lblNewLabel_2.setText(b);
33         }
34     }

 

用户登录,注册部分至此完毕。

实时显示人数,主要是向客户端返回存储socket对象的泛型数组大小。在当有新的客户端连接之后调用此方法,当有用户断开连接后调用此方法。

 1 public static void SendInfo(String rece, String AllName, String num) throws IOException {
 2         DataOutputStream outputStream = null;
 3         for (Socket Ssocket : Server.socketList) {
 4             outputStream = new DataOutputStream(Ssocket.getOutputStream());
 5             outputStream.writeUTF(num);
 6             outputStream.writeUTF(AllName);
 7             outputStream.writeUTF(rece);
 8             outputStream.flush();
 9         }
10     }

 

 

说说Bug

用户每次断开连接之前都没有先进行socket的关闭,服务端也没有移除相应的socket对象,这就导致当服务端再逐个发送至每个客户端,便找不到那个关闭的socket对象,会产生"write error" 。

所以便需要再客户端断开时移除相应的socket对象,查看java API文档,并没有找到在服务端可以判断客户端socket是否关闭的方方法。

 

 

 便想到了之前看的方法。(虽然感觉这样麻烦了一步,但没找到更好的办法)。于是在点击退出按钮,或关闭面板时向服务端发送一个"bye"字符,当服务端读取到此字符时便知道客户端要断开连接了,从而退出循环读取操作,移除对应的socket对象。

 1 面板关闭事件监听
 2 
 3 @Override
 4     public void windowClosing(WindowEvent arg0) {
 5         try {
 6             chat_Client.send("bye");
 7             File_O.file_O.readbye("bye");
 8         } catch (IOException e) {
 9             // TODO Auto-generated catch block
10             e.printStackTrace();
11         }
12     }
 1 退出按钮事件监听
 2 
 3 private class SwingAction extends AbstractAction {
 4         public SwingAction() {
 5             putValue(NAME, "退出");
 6             putValue(SHORT_DESCRIPTION, "关闭程序");
 7         }
 8         public void actionPerformed(ActionEvent e) {
 9             int result=optionPane.showConfirmDialog(contentPane, "是否关闭退出", "退出提醒", JOptionPane.YES_NO_OPTION);
10             if(result==JOptionPane.YES_OPTION) {
11                 try {
12                     chat_Client.send("bye"查看详情  

javasocket通信实现私聊群聊

   前言  闲言少叙,上代码!   代码编写   server服务端/***服务端*/publicclassServer{privatestaticServerSocketserver=null;privatestaticSocketss=null;/***客户端集合*/privatestaticMap<String,ServerThread>serve 查看详情

javasocket编程学习笔记

 1.Socket通信简介及模型  JavaSocket可实现客户端--服务器间的双向实时通信。java.net包中定义的两个类socket和ServerSocket,分别用来实现双向连接的client和server端。2.Socket通信实现方法 2.1 服务器端(非多线程)   用... 查看详情

javasocket如何实现客户端与客户端的交互?

我知道javasocket可以实现客户端与服务端的交互,可是貌似并不能给每个客户就行标识啊,就是一个客户和服务端进行交互,可是如何客户端和客户端就行交互呢?我知道可以先将客户端的消息在服务端读到然后再写给另一个客... 查看详情

javasocket实现两个客户段或多个客户端之间通信,该怎么解决

参考技术Ajavasocket有两种方式。一种是UDP这个可以直连,不需要服务器。一种是TCP这个是肯定要能过服务器来通信的。所以你说的。链接建立完毕后不再通过服务器!这个可以实现,但会麻烦一些。1.先说一下简单的点的吧。用TC... 查看详情

javasocket通信之客户端程序发送和接收数据(代码片段)

 JavaSocket通信(一)之客户端程序发送和接收数据网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类。通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据。对... 查看详情

javasocket编程学习笔记

在上一篇中,使用了javaSocket+Tcp/IP 协议来实现应用程序或客户端--服务器间的实时双向通信,本篇中,将使用UDP协议来实现Socket的通信。1.关于UDP  UDP协议(用户数据报协议)是无连接的、不可靠的、无序的,速度快,进行... 查看详情

nodejs实现服务端与客户端简单通信(代码片段)

...可以快速地搭建一个简单的Web服务器,实现服务端与客户端的简单通信。服务端实现过程引入http、fs、url三个模块使用createServer方法创建一个服务服务监听3000端口号当客户端向服务端发起请求时,服务端先进行路径解析&... 查看详情

nodejs实现服务端与客户端简单通信(代码片段)

...可以快速地搭建一个简单的Web服务器,实现服务端与客户端的简单通信。服务端实现过程引入http、fs、url三个模块使用createServer方法创建一个服务服务监听3000端口号当客户端向服务端发起请求时,服务端先进行路径解析&... 查看详情

javasocket编程实现群聊(超详细)(代码片段)

Javasocket编程实现群聊最终效果文末有完整代码,创作不易,点个赞再走吧~客户端之间的交流有人退出群聊时,减少在线人数实现流程1、项目结构即原理分析功能实现多客户之间聊天实时统计在线人数图形化界面创建... 查看详情

javasocket通信

原理:1.基于TCP协议,建立稳定连接的点对点的通信 a.实时,快速,安全性高,占用系统资源多,效率低2.请求-响应模式 a.客户端       在网络通讯中,第一次主动发起通讯的程序被称作客户端(Cl... 查看详情

java中socket实现最简单的客户端与服务端通信

Java中Socket实现最简单的客户端与服务端通信引言:因为最近项目中要接入某通信协议接口,基于TCP/IP的socket接口。于是就在本地弄一个最简单的Socket通信仅供学习。话不多说,直接开摆客户端代码packagetest;importjava.io.*;importjava.ne... 查看详情

网络编程_客户端与服务端简单代码实现

packagecom.qiliang.dmeo14_网络编程.Demo02;importjava.io.IOException;importjava.io.OutputStream;importjava.net.InetAddress;importjava.net.Socket;importjava.net.UnknownHostException;//客户端publicclassTCPClien 查看详情

java中socket实现最简单的客户端与服务端通信(代码片段)

Java中Socket实现最简单的客户端与服务端通信引言:因为最近项目中要接入某通信协议接口,基于TCP/IP的socket接口。于是就在本地弄一个最简单的Socket通信仅供学习。话不多说,直接开摆客户端代码packagetest;importjava.io.*;importjava.ne... 查看详情

java通过实现简单的tcp通信程序来理解tcp通信

...两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。 两端通信的步骤服务端程序,需要事先启动,等待客户端的连接。客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连... 查看详情

javasocket客户端和服务器端

客户端:packagecom.lzx.socket;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.PrintWriter;importjava.net.Socket;importjava.net.UnknownHostException;imp 查看详情

实现服务器和客户端数据交互,javasocket有妙招(代码片段)

...,它分为ServerSocket和Socket。本文分享自华为云社区《JavaSocket如何实现服务器和客户端数据交互》,作者:jackwangcumt。1Socket概述根据百度百科的定义,Socket译为套接字,它是对网络中不同主机上的应用 查看详情

javasocket客户端服务端对接正确写法(bio)(代码片段)

之前在工作中写过一些Socket客户端与服务端的代码,但是当时没有时间仔细研究,只能不报错先过的态度,对其细节了解不深,写的代码有各种问题也浑然不知,只是业务量级以及对接方对接代码没有出现出格的情况所以问题不... 查看详情