本篇文章将带你了解什么是网络编程?
网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。
网络编程中需要有发送端,接收端,也就是服务器和客户端进行数据交互。
目录
一、Socket套接字
1.1 概念
Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程~~
程序猿写网络程序,主要编写的应用层代码!!
真正要发送这个数据,需要上层协议,调用下层协议,应用层需要调用传输层。(想了解协议分层,请看【JavaEE】网络通信中的一些基本概念及协议分层_xyk:的博客-CSDN博客
传输层给应用层提供一组api,统称为socket api
1.2 分类
系统给程序猿提供了两组socket api
1.基于UDP的api(User Datagram Protocol)
2.基于TCP的api(Transmission Control Protocol)
1.3 TCP和UDP的特点
半双工:
二、DatagramSocket API
- Socket ==> 数据报的Socket对象
- Packet ==> 这个对象就是一个UDP数据报
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。
Socket,说明这个对象是一个socket对象,相当于对应到系统中一个特殊的文件(socket文件)
socket文件并非对应到硬盘上的某个数据存储区域,而是对应到,网卡这个硬件设备
(cmd中输出ipconfig查看):
有线网卡:
所以想要进行网络通信,就需要有socket文件这样的对象
借助这个socket文件对象,才能间接的操作网卡(遥控器)
DatagramSocket 构造方法:
方法签名 | 方法说明 |
DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口 (一般用于客户端) |
DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用 于服务端) |
举例说明一下:
DatagramSocket 方法:
方法签名 | 方法说明 |
void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该方法会阻 塞等待) |
void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发送) |
void close() | 关闭此数据报套接字 |
三、DatagramPacket API
DatagramPacket是UDP Socket发送和接收的数据报
DatagramPacket 构造方法:
方法签名 | 方法说明 |
DatagramPacket(byte[] buf, int length) | 构造一个DatagramPacket以用来接收数据报,接收的数据保存在 字节数组(第一个参数buf)中,接收指定长度(第二个参数 length) |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) | 构造一个DatagramPacket以用来发送数据报,发送的数据为字节 数组(第一个参数buf)中,从0到指定长度(第二个参数 length)。address指定目的主机的IP和端口号 |
DatagramPacket 方法
方法签名 | 方法说明 |
InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取 接收端主机IP地址 |
int getPort() | 从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获 取接收端主机端口号 |
byte[] getData() | 获取数据报中的数据 |
四、基于UDP Socket实现回显服务器
回显服务器(echo server)
客户端发了个请求,服务器返回一个一模一样的响应~~
三个核心工作:
- 读取请求并解析
- 根据请求求得对应响应(回显服务器 无此操作)
- 把响应返回给客户端
4.1 服务器端
4.1.1 创建Socket对象
4.1.2 绑定端口号
4.1.3 启动服务器主逻辑
需要先构造一个空饭盒并接收:
此时,如果还没有客户端发来的数据,怎么办?
- receive阻塞等待就行了,直到客户端发来真的数据请求
- 有点类似于阻塞队列~~
为了方便处理,把数据报转成String
根据请求计算响应(此处省略这个步骤)
把响应结果写回客户端,并打印日志
完整代码:
package network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author xyk的电脑
* @version 1.0
* @description: TODO
* @date 2023/4/6 16:48
*/
public class UdpEchoServer {
//需要先定义一个 socket 对象
//通过网络通信,必须要使用 socket对象
private DatagramSocket socket = null;
// 绑定一个端口, 不一定能成功!!
// 如果某个端口已经被别的进程占用了, 此时这里的绑定操作就会出错.
// 同一个主机上, 一个端口, 同一时刻, 只能被一个进程绑定.
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
//启动服务器的主逻辑
public void start() throws IOException {
System.out.println("服务器启动!");
while (true){
// 每次循环, 要做三件事情:
// 1. 读取请求并解析
// 构造空饭盒
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
// 食堂大妈给饭盒里面盛饭(饭从网卡上来的)
socket.receive(requestPacket);
// 为了方便处理这个请求, 把数据包转成 String
String request = new String(requestPacket.getData(),0,
requestPacket.getLength());
// 2. 根据请求计算响应(此处省略这个步骤)
String response = process(request);
// 3. 把响应结果写回到客户端
// 根据 response 字符串, 构造一个 DatagramPacket .
// 和请求 packet 不同, 此处构造响应的时候, 需要指定这个包要发给谁.
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
// requestPacket 是从客户端这里收来的. getSocketAddress 就会得到客户端的 ip 和 端口
response.getBytes().length,requestPacket.getSocketAddress());
socket.send(responsePacket);
System.out.printf("[%s:%d] req: %s, resp: %s\n",
requestPacket.getAddress().toString(),requestPacket.getPort(),
request,response);
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
udpEchoServer.start();
}
}
4.2 客户端
4.2.1 客户端启动,需要知道服务器在哪里!!
4.2.2 用户输入内容
4.2.3 把字符串构造成 UDP packet, 并进行发送.
4.2.4 把响应数据转换成 String 显示出来.
完整代码:
package network;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
/**
* @author xyk的电脑
* @version 1.0
* @description: TODO
* @date 2023/4/6 16:48
*/
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
// 客户端启动, 需要知道服务器在哪里!!
public UdpEchoClient(String serverIp,int serverPort) throws SocketException {
// 对于客户端来说, 不需要显示关联端口.
// 不代表没有端口, 而是系统自动分配了个空闲的端口.
socket = new DatagramSocket();
this.serverIp = serverIp;
this.serverPort = serverPort;
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while (true){
// 1. 先从控制台, 读取一个字符串过来
// 先打印一个提示符, 提示用户要输入内容
System.out.print("-> ");
String request = scanner.next();
// 2. 把字符串构造成 UDP packet, 并进行发送.
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
request.getBytes().length,
InetAddress.getByName(serverIp),serverPort);
socket.send(requestPacket);
// 3. 客户端尝试读取服务器返回的响应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
// 4. 把响应数据转换成 String 显示出来.
String response = new String(responsePacket.getData(),
0, responsePacket.getLength());
System.out.printf("req: %s, resp: %s\n", request, response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
udpEchoClient.start();
}
}
小结:
五、Socket文件怎么是存储数据的
六、测试udp回显服务器
七、udp英语查询服务器
package network;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
/**
* @author xyk的电脑
* @version 1.0
* @description: TODO
* @date 2023/4/9 21:39
*/
public class UdpDictServer extends UdpEchoServer {
private Map<String,String> dict = new HashMap<>();
public UdpDictServer(int port) throws SocketException {
super(port);
dict.put("dog","小狗");
dict.put("cat","小猫");
dict.put("fuck","卧槽");
}
@Override
public String process(String request){
return dict.getOrDefault(request,"该单词没有查到! ");
}
public static void main(String[] args) throws IOException {
UdpDictServer udpDictServer = new UdpDictServer(9090);
udpDictServer.start();
}
}