利用JavaUDPSocket+多线程模拟实现一个简单的聊天室程序
阅读原文时间:2023年07月12日阅读:1

对Socket的一点个人理解:Socket原意是指插座。家家户户都有五花八门的家用电器,但它们共用统一制式的插座。这样做的好处就是将所有家用电器的通电方式统一化,不需要大费周章地在墙壁上凿洞并专门接电线把家用电器和墙壁中的入户线接在一起。

将其对应于计算机中的网络通信:在TCP/IP网络的分层模型中,Socket是一组用于应用程序间进行网络通信的库函数,并位于应用层和传输层之间。有了Socket,一方面可以屏蔽底层通信软件和具体操作系统之间的差异,使任意两台安装了TCP/IP协议组件和套接字规范的计算机进行通信成为可能;另一方面,多种多样的应用程序就有了统一的网络编程的接口,而不需要和底层的传输层直接打交道,这就大大简化了网络编程的过程。

简单来说,Socket的通信机制就是,通信双方的应用程序都有Socket,将网络通信统一为Socket之间的通信,数据则在Socket之间通过IO流进行传输。

这里我们要模拟一个聊天室程序,众所周知,聊天室应用因其系统自身比较简单,不强调可靠性,因此适宜用UDP协议解决。

UDPSocket应用分为发送端和接收端两部分。对于发送端,该应用的执行流程为:

1.创建发送端Socket对象;

2.创建数据并打包;

3.发送数据;

4.释放传输资源。

对于接收端,该应用的执行流程为:

1.创建接收端Socket对象;

2.创建接收的数据包,换句话说将接收到的数据存放在何处;

3.接收数据;

4.解包,解析接收的数据,并打印在屏幕上;

5.释放传输资源。

此外,一个聊天室应用不仅要进行发送,也要进行接收,二者同时集成在同一应用上,因此必须通过多线程技术让发送和接收两个功能同时进行。

因此,对该程序的Java实现分为三部分:发送端线程,接收端线程,主函数。

发送端线程的Java代码实现为:

1 package chatroom;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.net.DatagramPacket;
7 import java.net.DatagramSocket;
8 import java.net.InetAddress;
9
10 public class SendThread implements Runnable {
11
12   private DatagramSocket ds;
13
14   public SendThread(DatagramSocket ds) {//发送端类的构造函数
15     this.ds = ds;
16   }
17
18   @Override
19   public void run() {
20     try {
21
22       BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//将用户从键盘输入的数据装入字符缓冲输入流
23
24       String line = null;
25       System.out.println("发送端:欢迎来到聊天室.你想在这里说些什么?");
26       while ((line = br.readLine()) != null) {
27         byte[] bys = line.getBytes();
28         DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.1.102"), 12306);//生成要发送的报文,在本例中发送端的目的地址就是本机
29         ds.send(dp);//发送数据报
30         if ("886".equals(line)) {//如果发送的数据是“886”,则说明聊天结束
31           break;
32         }
33      }
34
35      ds.close();
36     } catch (IOException e) {
37       e.printStackTrace();
38     }
39   }
40
41 }

接收端线程的Java代码实现为:

1 package chatroom;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6
7 public class ReceiveThread implements Runnable {
8   private DatagramSocket ds;
9
10   public ReceiveThread(DatagramSocket ds) {//接收端类的构造函数
11     this.ds = ds;
12   }
13
14   @Override
15   public void run() {
16     try {
17       while (true) {//死循环,一直到收到886后跳出
18         byte[] bys = new byte[1024];//将接收的数据存于此处
19         DatagramPacket dp = new DatagramPacket(bys, bys.length);//创建要接收的数据包
20         ds.receive(dp);//接收数据
21         String ip = dp.getAddress().getHostAddress();
22         String s = new String(dp.getData(), 0, dp.getLength());
23         System.out.println("接收端:from " + ip + " data is : " + s);//打印接收到的数据
24         if ("886".equals(s)) {
25           break;
26         }
27        }
28     } catch (IOException e) {
29       e.printStackTrace();
30     }
31   }
32
33 }

主函数的Java实现代码为:

1 package chatroom;
2
3 import java.io.IOException;
4 import java.net.DatagramSocket;
5
6 public class ChatRoom {
7   public static void main(String[] args) throws IOException {
8     DatagramSocket dsSend = new DatagramSocket();//发送端Socket
9     DatagramSocket dsReceive = new DatagramSocket(12306);//接收端Socket,接收端端口号设置为12306
10
11     SendThread st = new SendThread(dsSend);//创建发送端
12     ReceiveThread rt = new ReceiveThread(dsReceive);//创建接收端
13
14     Thread t1 = new Thread(st);//创建发送端线程
15     Thread t2 = new Thread(rt);//创建接收端线程
16
17     t1.start();
18     t2.start();
19    }
20 }

实现效果如下图所示:

关于Java自带的Socket库函数与Linux自带的Socket系统调用之间的区别:

首先,二者提供的接口不同;

第二,Java的Socket库函数底层执行原理是JVM将Java程序解析出来的XML参数传递给C++程序,并将响应由C++程序回传给Java程序;

第三,Java程序通过JVM机制保证了优良的跨平台特性,同样的程序可以在任何操作系统下执行,而Linux下自带的Socket编程,写出的程序只能在Linux下运行。