网络编程
UDP网络编程+IO+多线程 ---->聊天室
UDP是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常, 其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包, 如果数据包是否到达的消息及时反馈回来,那么网络就是通的。
思路:UDP实现单向数据发送,即一个发送方一个接收方。聊天需要双向发送消息,我们可以写两个类分别实现发送信息和接收消息且让他们实现 Runnable接口 ,这样聊天时同时运行发送消息和接收消息的线程。
Socket 套接字
发送端类
package SocketProgramming;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
//UDP消息发送不需要链接服务器
//发送端
public class TalkSend implements Runnable{
//建立一个Socket
DatagramSocket socket = null;
//IO流
BufferedReader reader = null;
//发出端口和接收目标的接收端口
private int fromPort;
private int toPort;
//接收目标IP
private String toIP;
public TalkSend(int fromPort, String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try {
socket = new DatagramSocket(fromPort);
//控制台读取文字 System.in
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
//读入数据
String data = reader.readLine();
//转为字节流
byte[] datas = data.getBytes();
//数据包:数据,数据长度,发送给谁(IP加端口)
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,
new InetSocketAddress(this.toIP,this.toPort));
//发送包
socket.send(packet);
//trim()去空格
if (data.trim().equals("bye")){
break;
}
}catch (Exception e){
e.printStackTrace();
}
}
//程序结束记得先断开连接释放系统资源
socket.close();
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收端类
package SocketProgramming;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
//接收端
public class TalkReceive implements Runnable{
//建立一个Socket
DatagramSocket socket = null;
//接收者的端口
private int port;
//标记发送者的名字
private String msgFrom;
public TalkReceive(int port,String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
socket = new DatagramSocket(port);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
//准备接收数据包
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0 ,container.length);
socket.receive(packet);//阻塞式接收包裹
//读取数据
byte[] data = packet.getData();
String receiveData = new String(data, 0 ,data.length);
System.out.println(msgFrom + ":"+receiveData);
if (receiveData.trim().equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//断开连接
socket.close();
}
}
两个测试类模拟两个人聊天
package SocketProgramming;
//学生和老师通信,两者都是接收方和发送方,调用两个线程接收和发送
public class TestTalkStudent {
public static void main(String[] args) {
//开启两个线程
new Thread(new TalkReceive(8888,"老师")).start();
new Thread(new TalkSend(7777,"localhost",9999)).start();
}
}
package SocketProgramming;
public class TestTalkTeacher {
public static void main(String[] args) {
//开启两个线程
new Thread(new TalkReceive(9999,"学生")).start();
new Thread(new TalkSend(5555,"localhost",8888)).start();
}
}
运行结果
其实和qq差不多,两个人同时上线聊天,一个人下线了还可以收到离线消息,两个人同时bye程序才结束
为什么close()
当我们new一个java流对象之后,不仅在计算机内存中创建了一个相应类的实例对象。而且,还占用了相应的系统资源,比如:文件句柄、端口、数据库连接等。在内存中的实例对象,当没有引用指向的时候,java垃圾收集器会按照相应的策略自动回收,但是却无法对系统资源进行释放。所以,我们需要主动调用close()方法释放java流对象。在java7之后,可以使用try-with-resources语句来释放java流对象,从而避免了try-catch-finally语句的繁琐,尤其是在finally子句中,close()方法也会抛出异常。
IO流需要强化学习!