一、多线程的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大数据学习视频礼包
来源:oschina
链接:https://my.oschina.net/u/4385799/blog/4337041