摘要:服务器端线程实现首先将服务器独立成一个线程服务器线程接受客户端连接请求在构造函数中我们初始化服务器的,然后等待客户端的连接。
这次在java实验的时候,要求使用server socket编写服务器和客户端的网络通信。最开始认为应该是挺简单的,但是后来发现低估了它。出现了不少的问题,所以也在这里与大家分享。
问题描述服务器程序的处理规则如下:初步实现
1) 向客户端程序发送Verifying Server!。
2) 若读口令次数超过3次,则发送Illegal User!给客户端,程序退出。否则向下执行步骤3)。
3) 读取客户端程序提供的口令。
4) 若口令不正确,则发送PassWord Wrong!给客户端,并转步骤2),否则向下执行步骤5)。
5) 发送Registration Successful!给客户端程序。客户端程序的处理规则如下:
1) 读取服务器反馈信息。
2) 若反馈信息不是Verifying Server!,则提示Server Wrong!,程序退出。否则向下执行步骤3)
3) 提示输入PassWord并将输入的口令发送给服务器。
4) 读取服务器反馈信息。
5) 若反馈信息是Illegal User!,则提示Illegal User!,程序退出。否则向下执行步骤6)
6) 若反馈信息是PassWord Wrong!,则提示PassWord Wrong!,并转步骤3),否则向下执行步骤。
7) 输出Registration Successful!。
首先,我们一定要清楚,这次和之前的程序不同,虽然都是在本地上,但是服务器和客户端需要两个启动程序来实现,以达到我们模拟远程连接的效果。
然后就是如何利用socket实现我们的功能了。
通过上面的图示,我们可以知道,首先需要先开启服务器,然后等待客户端的连接。
当客户端通过socket进行连接后,服务器端也会建立一个socket对象来帮助实现服务器和客户端的通信。
这时候就建立了一个TCP连接,我们就可以在服务器写数据,然后在客户端读取了,实现双方通信。
最后,当有一方决定通信结束,就会关闭连接。通信结束。
下面是初步实现的代码:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 服务器 */ public class ServerTest { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8080); Socket clientSocket = serverSocket.accept(); String welcome = "verifying server!"; OutputStream outputStream = clientSocket.getOutputStream(); outputStream.write(welcome.getBytes()); InputStream inputStream = clientSocket.getInputStream(); int time = 0; BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String password = bufferedReader.readLine(); // 获取登录信息,允许3次登录 while (time < 3) { if (password.equals("123")) { outputStream.flush(); outputStream.write("Registration Successful!".getBytes()); break; } else { outputStream.write("PassWord Wrong!".getBytes()); outputStream.flush(); password = bufferedReader.readLine(); time++; } } if (time >= 3) { outputStream.flush(); outputStream.write("Illegal User!".getBytes()); } outputStream.close(); clientSocket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
import java.io.*; import java.net.Socket; /** * 客户端 */ public class ClientTest { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 8080); String aline = new String(); // 获取输入流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); aline = bufferedReader.readLine(); // 访问失败 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 获取响应结果 String feedback = bufferedReader.readLine(); while (feedback == null || feedback.equals("PassWord Wrong!")) { if (feedback != null) { if (feedback.equals("PassWord Wrong!")) { System.out.println("PassWord Wrong!"); } else if (feedback.equals("Registration Successful!")) { System.out.println("Registration Successful!"); break; } } System.out.println("输入密码:"); // 输入密码 Scanner scanner = new Scanner(System.in); OutputStream outputStream = socket.getOutputStream(); String password = scanner.nextLine(); outputStream.write(password.getBytes()); feedback = bufferedReader.readLine(); } // 关闭连接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
初步实现后,运行:一片空白。什么都没有发生。
出问题了很正常,断点调试一下吧。发现问题所在:
客户端执行到这一行时,停止,然后服务器端接着执行;
同样的服务器端也到这行就停止了。
原因是服务器一方等着输入密码,而客户端一方等着响应结果,然后又没有开始输入密码。所以双方就这么一直等着,谁都不动了。
解决通过上面的分析,我们只要将僵局打破就能够解决问题。所以就先输入密码,然后再获取响应结果。这样就不会出问题了。
所以服务器端的代码并不需要做什么改动,只用修改客户端程序就行了。
import java.io.*; import java.net.Socket; /** * 客户端 */ public class ClientTest { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 8080); String aline = new String(); // 获取输入流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); aline = bufferedReader.readLine(); // 访问失败 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 主要修改这里,不先获取响应结果 // 初始化响应结果 String feedback = null; while (true) { if (feedback != null) { if (feedback.equals("PassWord Wrong!")) { System.out.println("PassWord Wrong!"); } else if (feedback.equals("Registration Successful!")) { System.out.println("Registration Successful!"); break; } } System.out.println("输入密码:"); // 输入密码 Scanner scanner = new Scanner(System.in); OutputStream outputStream = socket.getOutputStream(); String password = scanner.nextLine(); outputStream.write(password.getBytes()); feedback = bufferedReader.readLine(); } // 关闭连接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
成功实现!
升级:利用多线程实现功能成功实现之后,还不能满足于当前的状况。由于在实际使用socket的时候,很多时候都不是简单的一对一的情况,这种情况下,就需要使用多线程来帮助我们了。
使用多线程,我们可以实现一个服务器多个客户端的情况,每当有一个客户端请求连接,我们就会建立一个线程。
1.服务器端线程实现首先将服务器独立成一个线程:
/** * 服务器线程 */ public class ServerThread extends Thread { private ServerSocket serverSocket; private Socket clientSocket; public ServerThread(int port) { try { serverSocket = new ServerSocket(port); // 接受客户端连接请求 clientSocket = serverSocket.accept(); } catch (IOException e) { e.printStackTrace(); } } }
在构造函数中我们初始化服务器的socket,然后等待客户端的连接。接着就是最主要的部分了,线程主要执行的逻辑功能:run
@Override public void run() { try { // 提示连接成功 DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream()); dataOutputStream.writeUTF("verifying server!"); // 获取输入流 InputStream inputStream = clientSocket.getInputStream(); DataInputStream dataInputStream = new DataInputStream(inputStream); String password = dataInputStream.readUTF(); // 获取登录信息,允许3次登录 int time = 0; // 密码校验,允许输入3次 while (time < 3) { if (password.equals("123")) { dataOutputStream.writeUTF("Registration Successful!"); break; } else { dataOutputStream.writeUTF("PassWord Wrong!"); password = dataInputStream.readUTF(); time++; } } if (time >= 3) { dataOutputStream.writeUTF("Illegal User!"); } } catch (IOException e) { e.printStackTrace(); } }
run没有什么可以说的,主要就是实现服务器与客户端交互的时候执行的功能。然后在执行线程的时候,会自动调用run方法。
最后就是启动服务器端了:
/** * 服务器端 */ public class ServiceTest { public static void main(String[] args) { ServerThread serverThread = new ServerThread(8080); serverThread.start(); } }
同样,启用8080端口。
2.客户端线程实现类似服务器端,首先先建立客户端线程:
/** * 客户端线程 */ public class ClientThread extends Thread { private Socket socket; public ClientThread(String host, int port) { try { this.socket = new Socket(host, port); } catch (IOException e) { e.printStackTrace(); } } }
在构造函数中,建立客户端的socket对象。然后实现run方法。
@Override public void run() { try { // 获取输入流 DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); String aline = dataInputStream.readUTF(); // 连接服务器失败 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 获取响应结果 String feedback = null; // 进行密码输入 while (true) { if (feedback != null) { if (feedback.equals("PassWord Wrong!")) { System.out.println("PassWord Wrong!"); } else if (feedback.equals("Registration Successful!")) { System.out.println("Registration Successful!"); System.exit(1); } } System.out.println("输入密码:"); Scanner scanner = new Scanner(System.in); DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); String password = scanner.nextLine(); dataOutputStream.writeUTF(password); // 获取响应结果 feedback = dataInputStream.readUTF(); } } catch (IOException e) { e.printStackTrace(); } }
run方法中,主要实现了输入密码的功能。
最后就是启动客户端线程:
/** * 客户端 */ public class ClientTest { public static void main(String[] args) { ClientThread clientThread = new ClientThread("127.0.0.1", 8080); clientThread.start(); } }总结
这次的实验给我提了个醒,看似简单的东西,也永远不要轻视它。只有拿出应有的实例去解决问题,问题才是你眼中那个简单的问题。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/71987.html
摘要:具备发送和接受功能,在进行传输时,需要明确一个是发送端,一个是接收端。指定发送端口,不指定系统会随机分配。传输两个端点的建立连接后会有一个传输数据的通道,这通道称为流,而且是建立在网络基础上的流,称之为流。 端口:物理端口:逻辑端口: 用于标识进程的逻辑地址,不同进程的标识;有效端口:0~65535,其中0~1024系统使用或保留端口。 java 中ip对象:InetAddress....
摘要:绑定完成后允许套接字进行连接并等待连接。服务端根据报文返回响应,并关闭连接。单线程服务器多进程及多线程服务器复用服务器复用的多线程服务器单线程服务器一次只处理一个请求,直到其完成为止。 前言 本篇文章将涉及以下内容: IO实现Java Socket通信 NIO实现Java Socket通信 阅读本文之前最好了解过: Java IO Java NIO Java Concurrenc...
摘要:在设定时间内接收到相应操作的请求则返回可以处理请求的数量,否则在超时后返回,程序继续执行。使用接收请求并处理接收到请求后调用返回的集合。保存了处理当前请求的和,并提供了不同的操作类型。默认值为且其值必须小于的值。 Java中的Socket可以分为普通Socket和NioSocket两种。 普通Socket的用法 Java中的网络通信是通过Socket实现的,Socket分为Server...
摘要:为解决这问题,我们发现元凶处在一线程一请求上,如果一个线程能同时处理多个请求,那么在高并发下性能上会大大改善。这样一个线程可以同时发起多个调用,并且不需要同步等待数据就绪。表示当前就绪的事件类型。 JAVA NIO 一步步构建I/O多路复用的请求模型 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.com/jasonGeng88/blog 文章一:JAVA ...
阅读 3044·2021-11-22 09:34
阅读 3635·2021-08-31 09:45
阅读 3835·2019-08-30 13:57
阅读 1669·2019-08-29 15:11
阅读 1680·2019-08-28 18:04
阅读 3218·2019-08-28 17:59
阅读 1558·2019-08-26 13:35
阅读 2187·2019-08-26 10:12