java 网络编程之UDP通信和简单的群聊程序

时间:2023-03-09 08:28:07
java 网络编程之UDP通信和简单的群聊程序

*/

.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}

.hljs-comment,
.hljs-template_comment,
.diff .hljs-header,
.hljs-javadoc {
color: #998;
font-style: italic;
}

.hljs-keyword,
.css .rule .hljs-keyword,
.hljs-winutils,
.javascript .hljs-title,
.nginx .hljs-title,
.hljs-subst,
.hljs-request,
.hljs-status {
color: #333;
font-weight: bold;
}

.hljs-number,
.hljs-hexcolor,
.ruby .hljs-constant {
color: #099;
}

.hljs-string,
.hljs-tag .hljs-value,
.hljs-phpdoc,
.tex .hljs-formula {
color: #d14;
}

.hljs-title,
.hljs-id,
.coffeescript .hljs-params,
.scss .hljs-preprocessor {
color: #900;
font-weight: bold;
}

.javascript .hljs-title,
.lisp .hljs-title,
.clojure .hljs-title,
.hljs-subst {
font-weight: normal;
}

.hljs-class .hljs-title,
.haskell .hljs-type,
.vhdl .hljs-literal,
.tex .hljs-command {
color: #458;
font-weight: bold;
}

.hljs-tag,
.hljs-tag .hljs-title,
.hljs-rules .hljs-property,
.django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal;
}

.hljs-attribute,
.hljs-variable,
.lisp .hljs-body {
color: #008080;
}

.hljs-regexp {
color: #009926;
}

.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.lisp .hljs-keyword,
.tex .hljs-special,
.hljs-prompt {
color: #990073;
}

.hljs-built_in,
.lisp .hljs-title,
.clojure .hljs-built_in {
color: #0086b3;
}

.hljs-preprocessor,
.hljs-pragma,
.hljs-pi,
.hljs-doctype,
.hljs-shebang,
.hljs-cdata {
color: #999;
font-weight: bold;
}

.hljs-deletion {
background: #fdd;
}

.hljs-addition {
background: #dfd;
}

.diff .hljs-change {
background: #0086b3;
}

.hljs-chunk {
color: #aaa;
}

#container {
padding: 15px;
}
pre {
border: 1px solid #ccc;
border-radius: 4px;
display: block;
background-color: #f8f8f8;
}
pre code {
white-space: pre-wrap;
}
.hljs,
code {
font-family: Monaco, Menlo, Consolas, 'Courier New', monospace;
}
:not(pre) > code {
padding: 2px 4px;
font-size: 90%;
color: #c7254e;
background-color: #f9f2f4;
white-space: nowrap;
border-radius: 4px;
}
-->

UDP通信需要明确的几点:

  1. UDP通信不是面向连接的,发送端不管接收端是否启动是否能接收,发完数据报就结束。
  2. 无论是发送端还是接收端,都需要描述两个对象:套接字和数据报。
  3. 接收端的套接字对象中必须明确接收端口,且必须和发送端指定的目标端口一致。而发送端的套接字中则一般采用随机分配的发送端口。
  4. 无论是发送端还是接收端,数据报中都记录了自己和对方的socket信息(ip+port),还提供了用于发送或接收的数据缓冲区。这些数据只有数据报对象自己最清楚,如getPort(),getAddress(),getData()等。
    • (1).只不过对于发送端来说,创建发送报文对象需要指定目标套接字信息(ip+port),还需明确数据发送缓冲区。
    • (2).而对于接收端来说,则只需明确一个数据接收缓冲区即可。
  5. 接收端应该不断循环地负责接收。
  6. UDP套接字类DatagramSocket,UDP数据报类DatagramPacket。

UDPSender端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException; public class UDPSender { public static void main(String[] args) {
DatagramSocket dgs = null;
try {
// 1. 创建udp发送端的socket,一般使用随机发送端口
dgs = new DatagramSocket(); // 2. 创建udp报文包对象
// 2.1 创建数据发送缓冲区
String text = "Hello World! I'm coming";
byte[] buf = text.getBytes(); // 2.2 创建发送数据报文对象
InetSocketAddress isa = new InetSocketAddress("192.168.0.124",8888);
DatagramPacket dgp = new DatagramPacket(buf,buf.length,isa);
//DatagramPacket dgp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.0.124"), 8888); // 3.发送udp报文
dgs.send(dgp); } catch (SocketException e1) {
e1.printStackTrace();
} catch (UnknownHostException e2) {
e2.printStackTrace();
} catch (IOException e3) {
e3.printStackTrace();
} finally {
// 4. 关闭套接字
dgs.close();
}
}
}

UDPRecver端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException; public class UDPRecver { public static void main(String[] args) {
while(true){
// 1.创建udp接收套接字,接收端必须指定正确的端口
DatagramSocket dgs = null;
try {
dgs = new DatagramSocket(8888); // 2. 创建udp接收数据包对象
byte[] buf = new byte[1024];
DatagramPacket dgp = new DatagramPacket(buf, buf.length); // 3.从套接字中接收数据到数据包中
dgs.receive(dgp); // 4.展示udp发送端相关信息,包括发送的数据
String data = new String(dgp.getData(), 0, dgp.getLength());
String ip = dgp.getAddress().getHostAddress();
int port = dgp.getPort();
System.out.println("Data: "+data+" ip: "+ip+" port: "+port);
} catch (SocketException s) {
s.printStackTrace();
} catch (IOException i) {
i.printStackTrace();
} finally {
dgs.close();
}
}
}
}

UDP实现群聊:

思路:

  1. 一个线程负责发,一个线程负责收,因此使用多线程。
  2. 发送端的数据报目标端应该指定为广播目标。且发送的数据来源于键盘输入。
  3. 接收端要无限循环接收数据,但应该提供下线离开功能。(假设收到了"bye",就表示下线)
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException; public class QunChat {
public static void main(String[] args) throws SocketException {
//发送端和接收端套接字,接收端套接字端口为8888,需要传递给发送端的报文对象
DatagramSocket send_socket = new DatagramSocket();
DatagramSocket recv_socket = new DatagramSocket(8888); Sender sender = new Sender(send_socket,8888);
Recver recver = new Recver(recv_socket); Thread send_thread1 = new Thread(sender);
Thread recv_thread1 = new Thread(recver);
send_thread1.start();
recv_thread1.start();
}
} class Sender implements Runnable {
private DatagramSocket send_sock;
private int dest_port;
Sender(DatagramSocket s,int port){ //初始化时就指定目标端口
this.send_sock = s;
this.dest_port = port;
} public void run() {
while(true) {
try {
//群聊发送目标,以广播为例
InetSocketAddress isa = new InetSocketAddress("192.168.0.255", dest_port);
//从键盘接收数据
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line=bufr.readLine())!=null) {
//如果发送的是bye,则断开,且不发送给接收端
if(line.equals("bye")) break; byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length, isa);
send_sock.send(dp);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
send_sock.close();
}
}
}
} class Recver implements Runnable {
private DatagramSocket recv_sock;
Recver(DatagramSocket socket){
this.recv_sock = socket;
} public void run() {
while(true) {
try {
//接收报文对象
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
recv_sock.receive(dp);
String src_ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
if(data.equals("bye")) System.out.println(src_ip +" leaving");;
System.out.println("Recviving data: "+ data+" from "+src_ip );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!