这里客户端和服务器都分别使用了两个线程:
一个线程用于接收数据,一个线程用于发送数据,这样就可以实现别人一直发都行,你一直收也行。
如果只是一个线程用于收发数据,就只能你发一天,他收一条,然后他发一条,你收一条,这样不是很现实。
话不多说直接上程序,程序内有部分代码解释:
服务器端
/*
* TCP是面向连接的,在建立套接字的时候客户端套接字就要指定服务器的IP和端口号,
* 服务器只用指定服务器的端口号就行了。
*
* UDP是面向非连接的,本质上没有客户端和服务器之分,建立套接字时不需要指定IP地址,
* 但是有一个必须要指定端口号,他们之间靠数据报包产生联系,发送方需要将数据转化为字节数组,
* 注意只能是字节数组,然后打包为数据包,发送出去,发送包的时候指定目的IP和端口号,
* 这时接收方在建立套接字的时候就必须指定端口号了,这个端口号就是发送方在发送包时写的端口号,
* 如果双发都要发送数据,那么另外一方就都需要指定端口号。
* 接收方在接收数据的时候,也是通过包来接受的,先准备一个字节数组,作为包的参数,创建包,
* 然后接收数据,在解析数据
*
* 这里客户端和服务器都分别使用了两个线程,
* 一个线程用于接收数据,一个线程用于发送数据,这样就可以实现别人一直发都行,你一直收也行。
* 如果只是一个线程用于收发数据,就只能你发一天,他收一条,然后他发一条,你收一条,这样不是很现实。
* */
package mypackage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
public class UDPServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器开始运行");
// 创建套接字,指定服务器端口,这个端口和客户端发送包时的端口一致
DatagramSocket socket=new DatagramSocket(8080);
// 发数据线程
Thread t1 =new Thread(()->{
while (true){
// 收数据准备一个字节数组
byte[] buf2=new byte[1014];
// 创建包
DatagramPacket packet1=new DatagramPacket(buf2,buf2.length);
try {
// 接收数据包
socket.receive(packet1);
} catch (IOException e) {
e.printStackTrace();
}
// 返回接收包内数据的长度
int len=packet1.getLength();
// 转化为字符串,也可以String str1=new String(buf2,0,len);
String str1=new String(packet1.getData(),0,len);
// 打印收到的内容
System.out.printf("从客户端接收的数据:【%s】\n",str1);
}
});
//收数据线程
Thread t2 =new Thread(()->{
while (true){
// 发数据
// 键盘输入流
BufferedReader KeyboardIn=new BufferedReader(new InputStreamReader(System.in));
String str= null;
try {
// 读取键盘输入的内容
str = new String(KeyboardIn.readLine());
} catch (IOException e) {
e.printStackTrace();
}
// 将键盘输入的内容更改为字节数组
byte[] buf=str.getBytes();
DatagramPacket packet= null;
try {
//发送数据时,这个包一定要指定发送的地址,端口号与客户段的端口号一样
packet = new DatagramPacket(buf,buf.length, InetAddress.getByName("127.0.0.1"),7777);
} catch (UnknownHostException e) {
e.printStackTrace();
}
try {
// 发送包
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
});
// 启动线程
t1.start();
t2.start();
}
}
客户端
/*
* TCP是面向连接的,在建立套接字的时候客户端套接字就要指定服务器的IP和端口号,
* 服务器只用指定服务器的端口号就行了。
*
* UDP是面向非连接的,本质上没有客户端和服务器之分,建立套接字时不需要指定IP地址,
* 但是有一个必须要指定端口号,他们之间靠数据报包产生联系,发送方需要将数据转化为字节数组,
* 注意只能是字节数组,然后打包为数据包,发送出去,发送包的时候指定目的IP和端口号,
* 这时接收方在建立套接字的时候就必须指定端口号了,这个端口号就是发送方在发送包时写的端口号,
* 如果双发都要发送数据,那么另外一方就都需要指定端口号。
* 接收方在接收数据的时候,也是通过包来接受的,先准备一个字节数组,作为包的参数,创建包,
* 然后接收数据,在解析数据
*
* 这里客户端和服务器都分别使用了两个线程,
* 一个线程用于接收数据,一个线程用于发送数据,这样就可以实现别人一直发都行,你一直收也行。
* 如果只是一个线程用于收发数据,就只能你发一天,他收一条,然后他发一条,你收一条,这样不是很现实。
* */
//客户端程序和服务器程序基本一样,只是加了一点退出的判断指令
package mypackage;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
public class UDPClient {
public static void main(String[] args) throws IOException {
System.out.println("客户端开始运行");
// 创建套接字,指定客户端端口,这个端口和服务器发送包时的端口一致
DatagramSocket socket=new DatagramSocket(7777);
// 收数据线程
Thread t1 =new Thread(()->{
while (true){
// 收数据
byte[] buf2=new byte[1014];
DatagramPacket packet1=new DatagramPacket(buf2,buf2.length);
try {
socket.receive(packet1);
} catch (IOException e) {
// 这里选择break的原因是,下面发数据的线程有设定,如果输入bye,就关闭socket
// 关闭的话就会导致这个线程的异常,处理这个异常直接选择退出这个循环,正好可以结束程序
break;
}
int len=packet1.getLength();
String str1=new String(packet1.getData(),0,len);
System.out.printf("从服务器接收的数据:【%s】\n",str1);
}
});
Thread t2 =new Thread(()->{
while (true){
//发数据线程
// 发数据
BufferedReader KeyboardIn=new BufferedReader(new InputStreamReader(System.in));
String str= null;
try {
str = new String(KeyboardIn.readLine());
} catch (IOException e) {
e.printStackTrace();
}
byte[] buf=str.getBytes();
DatagramPacket packet= null;
try {
// 发送数据时,这个包一定要指定发送的地址
packet = new DatagramPacket(buf,buf.length, InetAddress.getByName("127.0.0.1"),8080);
} catch (UnknownHostException e) {
e.printStackTrace();
}
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
// 先把数据发出去,再判断,如果数据为bye,那么主动关闭套接字并退出循环
// 服务器那边一直等待不关闭
if(str.equals("bye")){
System.out.println("客户端停止");
socket.close();
break;
}
}
});
// 启动线程
t1.start();
t2.start();
}
}
结果测试