【✺网络编程】TCP和UDP编程

什么是Socket?

我们要了解什么是TCP编程与UDP编程,首先我们需要先了解什么是Socket。

Socket是一个抽象的概念,一个应用程序通过Socket建立一个远程连接,而Socket内部通过TCP/IP协议把数据传送至网络。

Socket就是套接字,由IP地址和端口号(范围是0~65535)组成,端口号是由操作系统随机分配的,它是一个0~65535之间的一个数字。小于1024的端口号是特权端口,一般都是需要管理员权限,但是大于1024的端口号可以被任意用户的应用程序打开。通过端口号,Socket才能正确连接本机中的应用程序。

当使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收。

因此,当Socket 连接成功地在服务器端和客户端之间建立后:

  • 对服务器端来说,它的Socket是指定的IP地址和指定的端口号。
  • 对客户端来说,它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。

TCP编程 (实现人机对话)

服务器端

首先我们先将需要和人机对话的内容存入一个Map集合用来存储问题与回答,当客户端发出问题时,如果有则返回相对应的值,如果没有,则返回null。

Map<String, String> chatMap = new HashMap<String, String>(){
    {
        put("你好", "你好呀");
        put("hi", "hi~");
        put("hello", "哈喽");
        put("吃了吗", "没呢,你呢");
        put("孤勇者", "爱你孤身走暗巷");
        put("有请潘周聃", "潘周聃,今年29岁,苏黎世理工大学.....");
        put("很高兴认识你", "我也是哦");
    }
};

然后,我们创建一个Socket对象,在服务器端通过Java的ServerSocket创建,传入一个监听端口,通过一个死循环使得服务器一直处于监听状态,使用accept()方法接收来自客户端的连接。接着创建一个输入流,并调用getInputStream()方法,获取来自客户端提出的问题。再创建一个输出流,将问题的回答反馈给客户端。

具体代码实现如下:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class ChatServer {
    public static void main(String[] args) {
        Map<String, String> chatMap = new HashMap<String, String>() {
            {
                put("你好", "你好呀");
                put("hi", "hi~");
                put("hello", "哈喽");
                put("吃了吗", "没呢,你呢");
                put("孤勇者", "爱你孤身走暗巷");
                put("有请潘周聃", "潘周聃,今年29岁,苏黎世理工大学.....");
                put("很高兴认识你", "我也是哦");
            }
        };

        try (ServerSocket server = new ServerSocket(9966)) {

            while (true) {
                // 发生客户端连接
                Socket client = server.accept();

                // 获取该客户端的IP地址
                String clientIP = client.getInetAddress().getHostAddress();

                try (BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
                     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))) {

                    // 获取该客户端的提问
                    String question = reader.readLine();
                    System.out.println("【服务器】 来自客户端" + clientIP + "提问:" + question);

                    // 获取该问题的答案
                    String answer = chatMap.get(question);
                    answer = answer == null ? "我不知道你在说什么!" : answer;

                    // 发送答案至客户端
                    writer.write(answer);
                    writer.flush();
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我们可以用一个简单的三元运算符来判定如果返回值为null时,可以输出指定的内容。

客户端

客户端相比于服务器端比较简单。

首先创建Socket对象,并传入服务器端的IP地址和端口号,通过Scanner获取来自用户提出的问题。然后创建输出流,通过getOutputStream()将问题传输给服务器端,再创建输入流用来获取来自服务器端的回答。

public class CharClient {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        while(true) {
            // 创建Socket,连接服务器
            try (Socket client = new Socket("192.168.254.153", 9966);
                 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
                 BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
                ){
                // 获取控制台的输入(问题)
                String question = input.nextLine();
                if(question.equals("over")) {
                    break; // 结束循环
                }

                // 发送问题至服务器
                writer.write(question);
                writer.flush();

                // 暂时结束本次输出
                client.shutdownOutput();

                // 获取来自服务器的“答案”
                String answer = reader.readLine();
                System.out.println("【客户端】 来自服务器的回答:" + answer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("本次问答结束!");
    }
}

UDP编程(简单的双人对话实现)

相对于TCP来说,UDP就简单很多,UDP不需要建立连接,也没有通过数据流传送,而是通过一个一个的数据包来实现。

UDP也是需要使用Socket来监听端口的的,不过它使用的是java提供的DatagramSocket。

发送端

发送数据时:通过setData()方法,将要发送的内容转换成字符串存储到定义的空数组中,再调用send()方法,实现数据的发送。

接收数据时:通过receive()方法,将接收到的内容传入之前receivePacket包定义的空数组,通过DatagramPacket返回的getData()、getOffset()和getLength()获取数据,并转成字符串,实现数据的接收。

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Scanner;

public class ChatB {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        // 客户端B监听7788端口
        try (DatagramSocket socket = new DatagramSocket(7788)){

            // 提前创建两个Packet数据包,分别用于发送和接收
            DatagramPacket sendPacket = new DatagramPacket(
                new byte[1024], 1024, // 数据
                new InetSocketAddress("192.168.1.5", 8899) // 目的地
            );
            DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);

            while(true) {
                // 发送
                System.out.print("我说:");
                String sendContent = input.nextLine();

                sendPacket.setData(sendContent.getBytes());
                socket.send(sendPacket);

                if(sendContent.equals("over")) {
                    System.out.println("你退出聊天!!!");
                    return;
                }

                // 接收
                socket.receive(receivePacket);
                String receiveContent = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
                if(receiveContent.equals("over")) {
                    System.out.println("对方已退出聊天!!!");
                    break;
                }
                System.out.println("她说:" + receiveContent);
            }

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

接收端

发送端与接收端思路整体一致,只不过接收端是先收再发。

public class ChatA {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        //客户端B监听8899端口
        try (DatagramSocket socket = new DatagramSocket(8899)){

            // 提前创建两个Packet数据包,分别用于发送和接收
            DatagramPacket sendPacket = new DatagramPacket(
                new byte[1024], 1024, // 数据
                new InetSocketAddress("192.168.1.5", 7788) // 目的地
            );
            DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);

            while(true) {
                // 接收
                socket.receive(receivePacket);
                String receiveContent = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
                if(receiveContent.equals("over")) {
                    System.out.println("对方已退出聊天!!!");
                    break;
                }
                System.out.println("她说:" + receiveContent);

                // 发送
                System.out.print("我说:");
                String sendContent = input.nextLine();
                sendPacket.setData(sendContent.getBytes());
                socket.send(sendPacket);
                if(sendContent.equals("over")) {
                    System.out.println("你退出聊天!!!");
                    return;
                }
            }

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

posted @   残城碎梦  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示