这是日常的练习,这种课题一般会在大学的实验课中出现。本文会贴出 TCP 实例和 UDP 实例的代码,并且在最后贴出使用 swing 编写的UI界面的局域网聊天软件。
TCP和UDP的区别
详细的区别不详细说,最大的特点就是,TCP是可靠的通信协议,UDP是不可靠的通信协议,但是TCP通信的成本比较高,而UDP的通信成本比较低。这就是有得有失,在网上不同的场景使用不同的协议,比如发送 Email 的时候要求一定是可靠,所以使用的是 TCP 协议,视频语音聊天的时候,要求传输速度要快,但是偶尔的卡顿也不影响正常使用,所以一般选择使用 UDP 协议。
使用java编写两种通信协议都不需要额外的导入其他jar包。
使用JAVA编写UDP协议的通信实例
发信部分
发信部分需要创建两个实例,DatagramSocket 和 DatagramPacket,另外还需要将信息转换为 byte 的形式,方便传送,代码如下:
- public class SocketSender {
- public static String sender(String msg) throws Exception {
- // 创建 datagramSocket对象,可以指定端口号和IP地址
- DatagramSocket sendSocket = new DatagramSocket();
- // 转换报文为byte类型
- byte[] buf = msg.getBytes();
- // 获取本机IP地址
- InetAddress ip = InetAddress.getLocalHost();
- int port = 1111;
- DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, ip, port);
- sendSocket.send(sendPacket);
- sendSocket.close();
- return "ok";
- }
- }
收信部分
收信部分与发信部分基本上一致,使用的也是之前的两个实例,只是发信使用的是 send()
方法,而收信使用的是 receive()
方法。
- public class SocketReceiver {
- public static String receiver(int port) throws Exception {
- // 获取本机IP地址
- InetAddress ip = InetAddress.getLocalHost();
- DatagramSocket receiver = new DatagramSocket(port, ip);
- byte[] recMsg = new byte[1024];
- DatagramPacket getPacket = new DatagramPacket(recMsg, recMsg.length);
- receiver.receive(getPacket);
- // 对接收到了 byte数组进行转换
- String res = new String(recMsg, 0, getPacket.getLength());
- receiver.close();
- return res;
- }
- }
接收端和发送端
收信和送信的类编写好了,就可以编写对应的接收端和发送端了,另外创建两个类,并分别编写可执行的main方法,此外在程序主体中使用了死循环的方式,可以让程序连续的监听消息或者发送消息。
发送端
- public class Client {
- public static void main(String[] args) {
- System.out.println("我是客户端,请输入要发送的信息");
- while (true) {
- Scanner sc = new Scanner(System.in);
- String msg = sc.next();
- String res = null;
- try {
- res = SocketSender.sender(msg);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println(res);
- }
- }
- }
收信端
- public class Server {
- public static void main(String[] args) {
- System.out.println("我是接收端,正在监听消息");
- while (true) {
- String res = null;
- try {
- res = SocketReceiver.receiver(1111);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("接收到消息,消息内容:" + res);
- }
- }
- }
使用JAVA编写TCP的通信实例
TCP的通信在 java 中使用,则是实例化了 socket ,发送数据和接收数据则是分别实例化 OutputStream 和 InputStream ,并对其进行操作。
发信部分
- public class TCPSender {
- public static String sender(String msg) throws Exception {
- // 首先获取IP地址和定义端口
- String host = "localhost";
- int port = 2222;
- Socket socket = new Socket(host, port);
- OutputStream os = socket.getOutputStream();
- os.write(msg.getBytes());
- socket.close();
- return "send ok!";
- }
- }
收信部分
收信部分使用了一个 accept()
方法,这个方法会停止程序,直到接收到数据才会继续往下执行。
- public class TCPReceiver {
- public static String receiver() throws Exception{
- ServerSocket ss = new ServerSocket(2222);
- Socket socket = ss.accept();
- InputStream is = socket.getInputStream();
- byte[] buf = new byte[1024];
- String res = new String(buf,0,is.read(buf));
- ss.close();
- return res;
- }
- }
接收端和发送端
与之前的UDP类似。直接在main方法中使用class方法即可
发送端代码
- public class Client {
- public static void main(String[] args) {
- System.out.println("我是客户端,请输入要发送的信息:");
- while (true) {
- Scanner sc = new Scanner(System.in);
- String msg = sc.next();
- String res = null;
- try {
- res = TCPSender.sender(msg);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println(res);
- }
- }
- }
接收端代码
- public class Server {
- public static void main(String[] args) {
- System.out.println("我是服务端,我接收数据,正在监听");
- while (true) {
- String res = null;
- try {
- res = TCPReceiver.receiver();
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("接收到信息:" + res);
- }
- }
- }
使用swing编写图形界面
在 eclipse 中想要编写 swing 的图形界面的程序,建议事先安装 eclipse 插件:WindowBuilder
原理分析
在同一个 UI 窗口中进行发信和收信的操作,必然要使用线程,否则无法实现同时接受和发送消息的功能。
java 线程的实现,可以直接让类实现 Runnable 的接口,并且在接口中的 run() 方法编写线程方法。但是另外的问题是,实现了 Runnable 接口无法通过传统的方式传入参数和输出参数。更优的解决方案是实现另外一个多线程的接口 Callable。但是,我这里还是坚持使用了前一种线程接口。传入参数,使用在线程类中设置私有类,并且设置了 GET 和 SET 方法,使用的时候直接通过 SET 方法传入参数,而输出,则是直接在类中操作前台的 UI 元件。
发送消息的类
- public class Sender implements Runnable{
- //使用get set 方法传入三个参数,发送的信息,ip地址和端口
- private String msg;
- private String host;
- private int port;
- public String getMsg() {
- return msg;
- }
- public void setMsg(String msg) {
- this.msg = msg;
- }
- public String getHost() {
- return host;
- }
- public void setHost(String host) {
- this.host = host;
- }
- public int getPort() {
- return port;
- }
- public void setPort(int port) {
- this.port = port;
- }
- public String sender(String msg,String host,int port) throws Exception{
- //首先获取IP地址和定义端口
- //创建socket
- Socket socket = new Socket(host, port);
- OutputStream os = socket.getOutputStream();
- //发信
- os.write(msg.getBytes("UTF-8"));
- //发送完毕,关闭socket
- socket.close();
- return "send ok!";
- }
- @Override
- public void run() {
- try {
- sender(this.getMsg(),this.getHost(),this.getPort());
- } catch (Exception e) {
- //打印错误
- KK.msgerror(e);
- e.printStackTrace();
- }
- }
- }
接收消息的类
- public class Receiver implements Runnable {
- // Runnable不能实现传入参数和传出参数,因此,使用 get set 方法塞入参数使用
- // port就是要传入的端口号参数
- private int port;
- public int getPort() {
- return port;
- }
- public void setPort(int port) {
- this.port = port;
- }
- public String receiver(int port) throws Exception {
- ServerSocket ss = new ServerSocket(port);
- // 进行TCP握手
- Socket socket = ss.accept();
- InputStream is = socket.getInputStream();
- // 创建数据缓冲区
- byte[] buf = new byte[1024];
- // 从is中读取信息,并且转换为string类型,编码符号是UTF-8
- String res = new String(buf, 0, is.read(buf), "UTF-8");
- ss.close();
- return res;
- }
- public void run() {
- try {
- // 使用循环,反复接受数据,否则只能接收一条数据
- while (true) {
- // 传入参数。用的get set 方法塞入的
- int port = this.getPort();
- // 获取到了消息
- String msg = receiver(port);
- // 调用KK中自定义编写的 static 方法来对外打印消息内容
- KK.addmsg(msg, port);
- }
- } catch (Exception e) {
- // 如果出现错误,调用KK中自定义编写的 static 方法,打印到屏幕上
- KK.msgerror(e);
- e.printStackTrace();
- }
- }
- }
最后则是主程序,图形的代码,由于代码涉及到很多图形组件的信息,都是自动生成的,废话比较多,所以挑选出几端比较重要的代码:
1.设置监听端口后点击开始监听按钮的代码
- btnNewButton_1.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- // 点击开始按钮之后,开启监听
- // 获取端口号
- String port = textField_3.getText();
- // 获取的端口号是string,转换为整形
- int intport = Integer.parseInt(port);
- // 启动监听
- try {
- // 实例化收信
- Receiver re = new Receiver();
- // 塞入端口号
- re.setPort(intport);
- // 创建线程
- Thread th = new Thread(re);
- // 启动线程
- th.start();
- // 打印消息
- textArea.append("开启端口号:" + port + ",正在监听消息;");
- textArea.append("\r\n");
- } catch (Exception e1) {
- e1.printStackTrace();
- }
- }
- });
2.点击发送消息按钮之后触发的代码
- btnNewButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- // 发信按钮按下之后
- // 获取对方ip、端口号,获取自己的端口号和消息内容
- String reip = txtLocalhost.getText();
- String report = textField_2.getText();
- String myport = textField_3.getText();
- String msg = textArea_1.getText();
- // 实例化发信
- Sender se = new Sender();
- // 塞入值
- se.setMsg(msg);
- se.setHost(reip);
- se.setPort(Integer.parseInt(report));
- // 创建线程
- Thread th = new Thread(se);
- // 启动线程
- th.start();
- // 获取当前时间
- Date date = new Date();
- DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- String time = df.format(date);
- // 打印信息
- textArea.append(time + " 我说:");
- textArea.append("\r\n");
- textArea.append(msg);
- textArea.append("\r\n");
- // 清空内容
- textArea_1.setText("");
- }
- });
3.另外编写了两个 static 方法,方便发信类和收信类使用
- // 静态方法,用来调用,往页面上添加消息
- public static void addmsg(String msg, int prot) {
- // 获取当前时间
- Date date = new Date();
- DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- String time = df.format(date);
- textArea.append(time + " 来自端口号" + prot + "的消息");
- textArea.append("\r\n");
- textArea.append(msg);
- textArea.append("\r\n");
- }
- // 静态方法,用来调用 往页面上添加错误信息
- public static void msgerror(Exception e) {
- textArea.append("出现错误,错误原因" + e);
- textArea.append("\r\n");
- }
软件的演示界面如下:
感谢你的赏识与认可
支付宝
微信支付
使用手机访问这篇文章
本文许可协议 © CC BY-NC-SA 4.0 转载请注明来源
可以像 TeamViewer 12 穿透内网吗?通讯时候支持端到端加密就好了
说到UDP,怎么测试某台电脑的UDP端口是否正常呢?
UDP端口被占用后,TCP就不能再次申请这个端口了吗?
用TCP端口测试程序可以测试UDP端口?
telnet 有点难用,不是像Ping即时回馈端口信息的
开了软防便是如此,不开又感觉不安全
高大上啊
总结得很不错。
博主有空能不能把这一个包打包发给我一下 ,我是才接触java的
望博主有空可以发一下完整源代码
我文章里面的所有内容就是完整源码。
请问最后这段主程序有完整的吗