作者主页:paper jie_博客****
本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。
本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将基础知识一网打尽,希望可以帮到读者们哦。
其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等
内容分享:本期将会分享网络编程相关的知识
目录
什么是网络编程
网络编程就是指网络上的主机,通过不同的进程,通过编程的方式来实现网络通信. 这里即便是一个主机,只要是不同的进程,基于网络来传输锁数据也叫网络编程.在开发过程中,一般都是一个主机运行多个进程来完成网络编程.网络编程的目的就是为网络上不同的主机来基于网络进行传输数据资源.
网络编程的基本概念
在进行网络数据传输的时候:
发送端: 就是数据发送方进程.发送端主机即为网络通信的源主机.
接收端: 及时数据接受方进程. 接收端主机即为网络通信的目标主机.
请求: 就是发送端发出的数据
响应: 就是接收端处理发送端的数据后再返回的数据
客户端: 就是获取服务的一方进程
服务器: 提供服务的进程,就称为服务端.
Socket
概念
Socket,是操作系统提供用来网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元.基于Socket的网络程序开发就是网络编程.
分类
Socket主要对传输层分为两类:
流套接字 - TCP协议: 它的特点就是有连接,可靠传输,面向字节流,有接收,发送缓冲区,不限制大小.
数据报套接字 - UDP协议: 它的特点就是无连接, 不可靠传输, 面向数据报, 有接收缓冲区,无发送缓冲区,大小有限制一次最多为64K.
Java数据报套接字通信模型
Java中使用UDP协议通信,主要基于DatagramSocket类来创建数据报接字,且使用DatagramPacket类来作为发送或者接收数据报.发送接收UDP数据报的流程如下:
Java流套接字通信模型
Socket注意事项
-
注意目的IP和端口号,标识了一次数据传输时发送数据的终端主机和进程.
-
如果端口被占用,启动进程就会报错,我们需要检查这个端口被哪个进程占用.我们可以通过端口号来查看进程的方式,可以在cmd中使用命令 netstat -ano | findstr 端口号的形式可以显示对应的进程Pid.然后我们可以任务管理器中通过Pid来查找进程,可以关闭不需要使用的进程.
-
Socket编程2我们使用的是流和数据报套接字,它是基于传输层的TCP或UDP协议,但这里我们应用层也要考虑.
UDP数据报套接字编程
API
DatagramSocket
DatagramSocket是UDP的Socket,用来发送和接受数据报的.
构造方法
|--------------------------|------------------------------------------------|
| 方法 | 说明 |
| DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定本机任意一个没有被使用的端口(一般用于客户端) |
| DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定指定的端口(port),(一般用于服务端) |
常用方法
|--------------------------------|----------------------------------|
| ⽅法 | 说明 |
| void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该 ⽅法会阻塞等待) |
| void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发 送) |
| void close() | 关闭此数据报套接字 |
DatagramPacket
DatagramPacket是UDP Socket用来发送和接收的数据报
构造方法
|---------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|
| ⽅法 | 说明 |
| DatagramPacket(byte[] buf,int length) | 构造⼀个DatagramPacket以⽤来接收数据报,接收的 数据保存在字节数组(第⼀个参数buf)中,接收指定 ⻓度(第⼆个参数length |
| DatagramPacket(byte[] buf,int offset,int length, SocketAddress address) | 构造⼀个DatagramPacket以⽤来发送数据报,发送的 数据为字节数组(第⼀个参数buf)中,从0到指定⻓ 度(第⼆个参数length)。address指定⽬的主机的IP 和端⼝号 |
常用方法
|--------------------------|---------------------------------------------|
| ⽅法签名 | ⽅法说明 |
| InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发 送的数据报中,获取接收端主机IP地址 |
| int getPort() | 从接收的数据报中,获取发送端主机的端⼝号;或从 发送的数据报中,获取接收端主机端⼝号 |
| byte getData() | 获取数据报中的数据 |
构造UDP发送的数据报的时候,需要传入SocketAddress,该对象可以使用InetSocketAddress来创建,它是SocketAddress的子类.
InetSocketAddress
|----------------------------------------------|-------------------------|
| ⽅法签名 | ⽅法说明 |
| InetSocketAddress(InetAddress addr,int port) | 创建⼀个Socket地址,包含IP地址和端⼝号 |
UDP代码示例
客户端
java
package UDP;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2024-01-23
* Time: 18:52
*/
public class UdpEchoClient {
private String serverIp;
private int serverport;
private DatagramSocket clientsocket = null;
public UdpEchoClient(String serverIp, int serverport) throws SocketException {
this.serverport = serverport;
this.serverIp = serverIp;
clientsocket = new DatagramSocket();
}
public void start() throws IOException {
System.out.println("客户端启动!!!");
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.print("-> ");
if(!scanner.hasNext()) {
break;
}
String request = scanner.next();
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
request.getBytes().length, InetAddress.getByName(serverIp), serverport);
clientsocket.send(requestPacket);
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
clientsocket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);
udpEchoClient.start();
}
}
服务端
java
package UDP;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2024-01-23
* Time: 18:53
*/
public class UdpEchoServer {
private DatagramSocket serverSocket = null;
public UdpEchoServer(int port) throws SocketException {
serverSocket = new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!!!");
while(true) {
//1.接收并解析请求
DatagramPacket datagramPacket = new DatagramPacket(new byte[4096], 4096);
serverSocket.receive(datagramPacket);
String request = new String(datagramPacket.getData(), 0, datagramPacket.getLength());//有效长度
String response = process(request);
//2.返回响应
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
datagramPacket.getSocketAddress());
serverSocket.send(responsePacket);
//3.打印日志
System.out.printf("[%s : %d] req: %s resp: %s\n", datagramPacket.getAddress(),
datagramPacket.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();
}
}
汉译英服务器
java
package UDP;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2024-01-23
* Time: 19:38
*/
public class UdpDictServer extends UdpEchoServer{
private HashMap<String, String> dict = new HashMap<>();
public UdpDictServer(int port) throws SocketException {
super(port);
dict.put("cat", "喵喵");
dict.put("dog", "狗叫");
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();
}
}
TCP流套接字编程
API
ServerSocket
ServerSocket是创建TCP服务端Socket的API.
构造方法
|------------------------|----------------------------|
| ⽅法 | 说明 |
| ServerSocket(int port) | 创建⼀个服务端流套接字Socket,并绑定到指定端⼝ |
常用方法
|-----------------|-------------------------------------------------------------------------|
| 方法 | 说明 |
| Socket accept() | 开始监听指定端⼝(创建时绑定的端⼝),有客⼾端 连接后,返回⼀个服务端Socket对象,并基于该 Socket建⽴与客⼾端的连接,否则阻塞等待 |
| void close() | 关闭此套接字 |
Socket
Socket是客户端Socket,或服务端中接收到客户端的连接的请求后,返回的服务端Socket.这里的Socket是保存对端的信息,以及用来和对对方收发数据的.
构造方法
|-------------------------------|-------------------------------------------|
| ⽅法 | 说明 |
| Socket(String host, int port) | 创建⼀个客⼾端流套接字Socket,并与目标IP的主机 上,目标端⼝的进程建⽴连接 |
常用方法
|--------------------------------|-------------|
| ⽅法签名 | 说明 |
| InetAddress getInetAddress() | 返回套接字所连接的地址 |
| InputStream getInputStream() | 返回此套接字的输⼊流 |
| OutputStream getOutputStream() | 返回此套接字的输出流 |
TCP代码示例
服务端
java
package TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2024-01-24
* Time: 14:25
*/
public class TcpEchoServer {
private ServerSocket serverSocket = null;
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService service = Executors.newCachedThreadPool();
while(true) {
Socket clientSocket = serverSocket.accept();
//使用线程池
service.submit(() -> {
try {
processConnection(clientSocket);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
//创建多线程
/*Thread t = new Thread(() -> {
try {
processConnection(clientSocket);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
t.start();
}*/
}
}
public void processConnection(Socket clientSocket) throws IOException {
try(InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
System.out.printf("[%s : %d] 客户端上线!!!\n", clientSocket.getInetAddress(), clientSocket.getPort());
Scanner scanner = new Scanner(inputStream);
PrintWriter writer = new PrintWriter(outputStream);
while(true) {
//1. 读取请求并解析
if(!scanner.hasNext()) {
System.out.printf("[%s : %d] 客户端下线!!!", clientSocket.getInetAddress(), clientSocket.getPort());
break;
}
String request = scanner.next();
String response = process(request);
//2. 返回响应
writer.println(response);
writer.flush();
//3. 打印日志
System.out.printf("[%s : %d] req: %s resp: %s\n", clientSocket.getInetAddress(),
clientSocket.getPort(), request, response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
clientSocket.close();
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
tcpEchoServer.start();
}
}
客户端
java
public class TcpEchoClient {
private Socket socket = null;
public TcpEchoClient(String serverIp, int serverPort) throws IOException {
socket = new Socket(serverIp, serverPort);
}
public void start() {
System.out.println("客户端启动");
try(InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
Scanner scannerConsole = new Scanner(System.in);
Scanner scannerNetwork = new Scanner(inputStream);
PrintWriter writer = new PrintWriter(outputStream);
while(true) {
System.out.print("-> ");
if(!scannerConsole.hasNext()) {
break;
}
String request = scannerConsole.next();
//1. 发送请求
writer.println(request);
writer.flush();
//2. 接受响应
String response = scannerNetwork.next();
//3. 打印响应
System.out.println(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);
tcpEchoClient.start();
}
}