【✺网络编程】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();
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了