一、网络编程
什么是网络编程?
我们打开视频网站,比如腾讯视频,看仙逆,剑来等动漫,实质上是通过网络,获取到网络上的一个视频资源。
与在本地打开一个视频文件一样,只是这个视频文件来源于网络,相对于本地,网络提供了更为丰富的网络资源。
1.1、网络编程
网络编程是指通过编写程序来实现计算机之间的通信和数据交换。它涉及使用网络协议(如 TCP/IP、UDP等)在网络上发送和接收数据。(网络上的主机,通过不同的进程,以编码的方式实现网络通信)
即便是同一个主机,只要进程不同,基于网络来传输数据,也属于网络编程。
1.2、网络编程的核心概念
1.2.1、客户端和服务端
服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端。
客户端:获取服务的一方进程,称为客户端。
1.2.2、Socket套接字
概念:Socket套接字是由系统提供用于网络通信的基本操作单元。
基于Socket套接字的网络程序开发就是网络编程。
主要分为以下三类:
(1)流套接字
使用传输层TCP协议
TCP(Transmission Control Protocol)传输控制协议
特点:
• 有连接
• 可靠传输
• 面向字节流
• 有接受缓存区,也有发送缓存区
• 大小不限
字节流:基于IO流以字节为单位进行数据传输,数据在流未关闭的情况下可以持续发送和接受.
(2)数据报套接字
使用传输层UDP协议
UDP(User Datagram Protocol)用户数据报协议
特点:
• 无连接
• 不可靠传输
• 面向数据报
• 有接受缓存区,无发送缓存区
• 大小受限:一次最多传输64K
数据报:传输是原子的,发送和接受操作必须一次性完成,发送方需将数据封装成一个数据报,接收方则接受整个数据报。
(3)原始套接字
原始套接字(Raw Socket)是一种允许应用程序直接访问网络层协议的套接字类型。与常见的流式套接字(如TCP)或数据报套接字(如UDP)不同,原始套接字允许用户自定义网络层协议头,甚至可以直接发送和接收原始数据包。(此知识我们不过多讲解)
1.3、数据报套接字通信模型
上文UDP,即用户数据报协议是无连接,面向数据报的特征,每次都是没有建立连接,并且一次性发送所有数据报,一次性接受所有数据报。
Java中使用UDP协议通信,主要是基于DatagramPacket类来创建数据报套接字,并使用DatagramPacket作为发送或接受的UDP数据报,对于一次发送及接受UDP数据报的流程如下:
1.4、流套接字通信模型
TCP,即传输控制协议是有连接的,基于IO流,持续发送和接收的协议。
Java使用TCP协议通信,在服务端创建ServerSocket并绑定端口,创建Socket连接ServerSocket对象接受客户端的Socket对象调用方法实现通信;在客户端创建Socket对象指定服务端地址和端口。
不论是客户端和服务端都要在通信完成后关闭流对象,服务端要多关闭一个Socket对象,客户端的Socket对象会随通信完成后进程的结束而自动关闭~~
流程如下 :
1.5、 Socket编程注意事项
(1)客户端和服务端:开发时,经常是基于一个主机开启两个进程作为客户端和服务端,但在真实情况下。一般都是不同主机。
(2)目的IP和端口号,标识了一次数据传输时要发送数据的终点主机和进程。
(3)Socekt编程是我们使用流套接字和数据报套接字,基于传输层的TCP或UDP协议,但在应用层协议,也需要考虑(后续更新)。
(4)端口占用,如果一个进程Q已经绑定了一个端口,在启动一个进程P绑定该端口,就会报错,报错信息如下:
我们可以在 cmd 中输入 netstat -ano | findstr 端口号,可以显示对应进程的 pid :
然后在任务管理器中,通过 pid 查看进程 :
解决端口被占用的问题:
(1)如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B.
(2)如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口。
二、 UDP数据报套接字编程
2.1、API介绍
DatagramSocket(通信端点)
DatagramSocket是UDP Socket,用于发送和接收UDP数据报。
构造方法
DatagramSocket 方法
DatagramPacket(数据容器)
DatagramPacket是UDP Socket发送和接收UDP数据报
构造方法
DatagramPacket方法
InetSocketAaddress
InetSocketAaddress是(SocketAddress的子类)构造方法
2.2、模拟实现服务器
2.2.1、完整代码
package UdpEcho;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.sql.PreparedStatement;
public class UdpEchoServer {
private DatagramSocket datagramSocket=null;
public UdpEchoServer(int port) throws SocketException {
datagramSocket=new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
while(true){
DatagramPacket requestPacket=new DatagramPacket(new byte[4048],4048);
datagramSocket.receive(requestPacket);
String request=new String(requestPacket.getData(),0,requestPacket.getLength());
String response=procsee(request);
DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
datagramSocket.send(responsePacket);
System.out.printf("[%s:%d] req:%s rep:%s/n",
requestPacket.getAddress(),requestPacket.getPort(),request,response);
}
}
public String procsee(String s){
return s;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server=new UdpEchoServer(8776);
server.start();
}
}
2.2.2、核心组件
2.2.3、服务启动流程
2.2.4、与客户端交互流程图
2.3、模拟实现客户端
2.2.1、完整代码
package UdpEcho;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpEchoClient {
private String IP;
private int Port;
private DatagramSocket socket=null;
public UdpEchoClient(String ip,int port) throws SocketException {
this.IP=ip;
this.Port=port;
socket=new DatagramSocket();
}
public void start() throws IOException {
Scanner sc=new Scanner(System.in);
while(true) {
if (!sc.hasNext()){
break;
}
System.out.println("请输入您要发送的内容:");
String request=sc.next();
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(IP),Port);
socket.send(requestPacket);
DatagramPacket responsePacket=new DatagramPacket(new byte[4048],4048);
socket.receive(responsePacket);
String response=new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client=new UdpEchoClient("127.0.0.1",8776);
client.start();
}
}
2.2.2、核心组件
UDP是无连接的,故要定义IP和端口号,构造方法中Socket未指定本地端口号,由系统自动分配以避免冲突。
private String IP;
private int Port;
private DatagramSocket socket=null;
public UdpEchoClient(String ip,int port) throws SocketException {
this.IP=ip;
this.POrt=port;
socket=new DatagramSocket();
}
2.2.3、通信流程
public void start() throws IOException {
Scanner sc=new Scanner(System.in);
while(true) {
if (!sc.hasNext()){
break;
}
System.out.println("请输入您要发送的内容:");
String request=sc.next();
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(IP),POrt);
socket.send(requestPacket);
DatagramPacket responsePacket=new DatagramPacket(new byte[4048],4048);
socket.receive(responsePacket);
String response=new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}