一、多线程的TCP网络编程
- 如果需要进行多次数据交互,就可以在程序中设置一个循环,不断向对方发送请求,即可完成多次数据交互。同样,如果需要让服务器同时响应多个客户端的请求,可以使用多线程的方法,也就是服务器端没接收到一个新的连接请求,就启动一个专门的线程与客户端进行交互。
- 本测试程序可以分为三类:客户端类、服务器类和逻辑线程类
- 首先是客户端类
package com.bjpowernode.java_learning; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; import java.util.Scanner; public class D127_1_ClientTest { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String input = null; Socket socket = null; DataInputStream in = null; DataOutputStream out = null; String serverIP = "127.0.0.1"; //服务器端IP int port = 5050; try { socket = new Socket(serverIP,port); //连接服务器 in = new DataInputStream(socket.getInputStream()); //创建输入流 out = new DataOutputStream(socket.getOutputStream());//创建输出流 System.out.println("请输入一个待计算的四则运算表达式"); while(scanner.hasNext()) { input = scanner.nextLine(); //从键盘输入一个待计算的四则运算表达式 if(!input.contentEquals("0")) { out.writeUTF(input); //向服务器发送运算请求 String result = in.readUTF(); //等待读取运算结果 System.out.println("服务器返回的计算结果:"+result); System.out.println("请输入一个正整数的四则运算表达式(输入0退出):"); }else { break; //请求结果 } } }catch(Exception e) { System.out.println("与服务器连接中断"); }finally { try { in.close(); //关闭网络连接 out.close(); socket.close(); System.out.println("连接结束"); }catch(Exception e) { } } } }
- 接下来是服务器端类
package com.bjpowernode.java_learning; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import com.bjpowernode.java_learning.D127_3_ThreadTest; public class D127_2_ServerTest { public static void main(String[] args) { ServerSocket server_socket = null; Socket socket = null; int port = 5050; while(true) { try { server_socket = new ServerSocket(port); System.out.println("服务器启动!"); }catch(IOException e1) { System.out.println("正在监听"); //ServerSocket对象不能重复创建 } try { System.out.println("等待客户请求!"); socket = server_socket.accept(); System.out.println("客户的地址:"+socket.getInetAddress() + ":" + socket.getPort()); }catch(IOException e) { System.out.println("正在等待客户"); } if(socket!=null) { new D127_3_ThreadTest(socket); //为每个客户启动一个专门的线程 } } } }
- 最后是逻辑线程类
package com.bjpowernode.java_learning; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; public class D127_3_ThreadTest extends Thread { Socket socket = null; DataInputStream in = null; DataOutputStream out = null; String str; String response; String ip; int port; public D127_3_ThreadTest(Socket socket) { this.socket = socket; start(); } public void run() { try { in = new DataInputStream(socket.getInputStream()); //创建输入流 out = new DataOutputStream(socket.getOutputStream()); //创建输出流 ip = socket.getInetAddress().getHostAddress(); //客户端IP地址 port = socket.getPort(); //客户端的端口号 while(true) { str = in.readUTF(); //获取客户端的表达式 System.out.println("客户端" + ip + ":" + port + "发送的请求内容:"); System.out.println(str+"=?"); if(str.contentEquals("0")) { System.out.println("连接结束"); break; }else { response = doComputer(str); //对表达式进行计算 out.writeUTF(response); //响应计算结果 } } }catch(Exception e) { System.out.println("连接结束"); }finally { try { in.close(); out.close(); socket.close(); }catch(Exception e) { } } } public String doComputer(String str) { String input; String[] sym; String[] data; int a=0,b=0,result=0; input = str; data = input.split("\D+"); sym = input.split("\d+"); a = Integer.parseInt(data[0]); b = Integer.parseInt(data[1]); try { switch(sym[1]) { case "+": result = a + b; break; case "-": result = a - b; break; case "*": result = a * b; break; case "/": result = a / b; } System.out.println("计算结果:"+input+"="+result); return String.valueOf(result); }catch(java.lang.ArithmeticException e) { System.out.println("数据错误!"); return "数据错误"; } } }
- 这样我们就做完了所有的工作,那么接下来进行启动该项目
- 首先启动服务器的类,运行结果如下:
- 然后启动客户端类
- 我们在客户端类中cosole界面输入
3*5
然后回车
- 从上面的运行结果来看,得到了我们想要的结果,那么接下来输入
0
来结束这个项目的运行
总结:在编写代码过程中我们遇到的问题,或者以前忘记的知识点:
1.服务器端类中:
(1)server_socket.accept() 一直处于阻塞直到接收了socket发来的请求,并返回一个socket对象;
(2)写一了一个多线程类,来给每个客户一个线程;
2.客户端类 (1)DataInputStream(socket.getInputStream()) socket的成员方法来获取输入流,并返回一个InputStream对象,然后经过java.io.DataInputStream方法转换为专有的流。
(2)DataOutputStream对象.writeUTF(String result) 用于传入字符串
(3)DataInputStream对象.readUTF() 用于读取字符串
3.多线程类
(1) 多线程重写了run方法
(2) 客户端和服务器端的读和写是正相反的。
二、源码:
- D127_1_ClientTest.java
- D127_2_ServerTest.java
- D127_3_ThreadTest.java
- https://github.com/ruigege66/Java/blob/master/D127_1_ClientTest.java
- https://github.com/ruigege66/Java/blob/master/D127_2_ServerTest.java
- https://github.com/ruigege66/Java/blob/master/D127_3_ThreadTest.java
- CSDN:https://blog.csdn.net/weixin_44630050
- 博客园:https://www.cnblogs.com/ruigege0000/
- 欢迎关注微信公众号:傅里叶变换,个人账号,仅用于技术交流,后台回复“礼包”获取Java大数据学习视频礼包