3.1 多线程实现双人聊天
- 首先要想清楚,为什么 要多线程 实现 双人的 聊天。而不是用 线程 去写!!!
单线程的话,我们只能够 方便的操作 一个功能(发送 / 接收),即使 你把 发送 和 接收 写在 一起!甚至是 经过了 死循环 和 标识判断,实现了 两种功能 的切换,但在效率上 也是很低的。(并不是 不能做到!而是 很复杂,很不方便,即使实现了,效率也很低!)
我一开始 学习 网络编程 就尝试 写过 一个 主线程 写 接收 和 发送 功能,可以写。但是 特别的麻烦,效率还低下。
- 如果可以实现,那么应该 主要的核心点是什么 ?
是可以实现的,不过需要注意的是,必须在 UDP 协议下 使用 四个 端口!这是为什么呢 ? 也不是必须。。只是 让其 不产生 不必要的冲突!!我们的端口那么多。开四个 没啥事。
这四个 端口的 分配是:
聊天者A:自身发送端口、目标接收端口、自身接收端口。
聊天者B:自身发送端口、目标接收端口、自身接收端口。
总计:目标接收端口 *2 + 自身发送 端口 * 2 一共是 4个 端口。这样 就可以保证,我跟你 断开连接,断开的是 我的发送和你的接收 端口!这个时候 你还可以 跟我 发送 信息。就证明 这种 4 个 端口的方式 是非常的 稳定的!!!
代码很简单,只要 简单的学习过 JavaThread 就可以 写出来。基本上 就是 面向 CV(复制粘贴) 的编程工作。
- 发送信息事务(要用线程 启动)
package www.muquanyu.lesson03;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ReseiveThread implements Runnable{
private int fromPort;//就是自己的 端口
private DatagramSocket socket = null;
public ReseiveThread (int fromPort) throws SocketException {
socket = new DatagramSocket(fromPort);
}
@Override
public void run() {
while(true)
{
//创建接收数据包
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0,container.length);
//接收 数据包
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
byte[] data = packet.getData();
System.out.println(getSize(data));
String dataString = new String(data,0,getSize(data));
if(dataString.equals("bye"))
{
break;
}
dataString = "接收到了msg:"+ dataString;
System.out.println(dataString);
}
socket.close();
System.out.println(fromPort+":UDP协议已关闭");
}
public static int getSize(byte[] data) {
int i = 0;
for (byte x : data) {
if (x != (byte) 0) {
i++;
}
}
return i;
}
}
- 接收信息事务(要用线程启动)
package www.muquanyu.lesson03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
public class SendThread implements Runnable {
private int fromPort;//自己的 端口
private int toPort;//目标的 端口
private InetAddress address = null;//目标的 IP 地址
private DatagramSocket socket = null;
public SendThread(String address,int fromPort, int toPort) throws UnknownHostException, SocketException {
this.fromPort = fromPort;
this.toPort = toPort;
this.address = InetAddress.getByName(address);
this.socket = new DatagramSocket(fromPort);
}
@Override
public void run() {
while(true)
{
//准备数据:控制台读取 System.in
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String data = null;
try {
data = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress(address, toPort));
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
if(data.equals("bye"))
{
break;
}
}
socket.close();
System.out.println(fromPort+":UDP协议已关闭");
}
}
接下来 就是 直接 调用 咱们写的 线程事务,来进行 线程的启动就行了。
我们可以再创建 两个 类,分别代表 聊天者 A 和 聊天者 B。它们各自 都启动 两个线程,总计 四个线程!!(效率是极高的。)
- 聊天者 A
package www.muquanyu.lesson03;
import java.net.SocketException;
import java.net.UnknownHostException;
public class SenderA {
public static void main(String[] args) throws SocketException, UnknownHostException {
ReseiveThread reseiveThread = new ReseiveThread(8888);
SendThread sendThread = new SendThread("localhost",6666, 9999);
new Thread(reseiveThread).start();
new Thread(sendThread).start();
}
}
- 聊天者 B
package www.muquanyu.lesson03;
import java.net.SocketException;
import java.net.UnknownHostException;
public class SenderB {
public static void main(String[] args) throws SocketException, UnknownHostException {
ReseiveThread reseiveThread = new ReseiveThread(9999);
SendThread sendThread = new SendThread("localhost",7777, 8888);
new Thread(reseiveThread).start();
new Thread(sendThread).start();
}
}