Socket
Socket
(套接字 )是用于网络通信的编程接口、网络通信的基础,通过它可以实现不同计算机之间的数据传输,应用程序可以通过它发送或接收数据;就像操作文件那样可以打开、读写和关闭。它提供了一种机制,使得计算机之间可以进行数据的发送和接收。
套接字允许应用程序将 I/O 应用于网络中,并与其他应用程序进行通信。网络套接字是 IP 地址与端口的组合。
Socket
可以用于不同层次的通信,包括应用层、传输层和网络层。它可以通过 计算机网络中的 TCP协议 或 UDP协议 进行通信。
-
TCP协议 提供面向连接的可靠数据传输,确保数据的可靠性和顺序性
-
UDP协议 则是无连接的、不可靠的数据传输,适用于实时性要求较高、数据包大小较小的场景。
Socket 编程
在 Socket
编程中,一个计算机可以作为服务器端或客户端。服务器端创建一个 Socket
并绑定到特定的 IP地址 和端口,然后开始监听来自客户端的连接请求。客户端创建一个 Socket
并连接到服务器端指定的IP地址和端口。一旦连接建立,服务器端和客户端之间可以通过 Socket
进行数据的发送和接收。
Socket
编程可以实现各种网络应用,如 Web 服务器、邮件服务器、聊天应用等。它提供了简单而强大的接口,使得网络通信变得相对容易。
Java Socket
Java 提供了 java.net
包来支持 Socket
编程,通过它可以在 Java 程序中创建和操作 Socket
。
Socket
在 Java 中,可以使用 Socket类
来创建客户端 Socket
,它提供了与服务器端进行通信的方法,可以指定服务器的主机名(或IP地址)和端口号来建立连接。一旦连接建立成功,就可以使用 Socket
对象的InputStream
和 OutputStream
来进行数据的读写操作。
构造方法
在 Java 中,Socket
类提供了多个构造方法,用于创建 Socket
对象。
- 用指定的主机名(或IP地址)和端口号创建一个客户端
Socket
对象。该构造方法用于与服务器建立连接。
java
Socket(String host, int port)
host
:主机名port
:端口号
- 用指定的服务器地址和端口号创建一个客户端
Socket
对象。该构造方法接受一个InetAddress
对象,表示服务器的 IP 地址。
java
Socket(InetAddress address, int port)
- 用指定的主机名(或IP地址)、端口号、本地 IP 地址和本地端口号创建一个客户端
Socket
对象。本地 IP 地址和本地端口号参数可以用于指定客户端用于通信的网络接口。
java
Socket(String host, int port,
InetAddress localAddr, int localPort)
- 用指定的服务器地址、端口号、本地 IP 地址和本地端口号创建一个客户端
Socket
对象。本地 IP 地址和本地端口号参数可以用于指定客户端用于通信的网络接口。
java
Socket(InetAddress address, int port,
InetAddress localAddr, int localPort)
- 创建一个未连接的、空的
Socket
对象。该构造方法常用于服务器端Socket
的创建,通过调用accept()
方法接受客户端的连接并创建新的Socket
对象。
java
Socket()
- 用指定的主机名(或IP地址)和端口号创建一个
Socket
对象,并根据stream
参数的值选择流式套接字 (TCP) 或数据报套接 (UDP)。
java
Socket(String host, int port, boolean stream)
- 使用指定的代理服务器创建一个
Socket
对象。可以使用此构造方法在连接到服务端之前与代理服务器建立连接。
java
Socket(Proxy proxy)
常用方法
Socket
类提供了一系列方法来实现网络通信。以下是一些常用的 Socket 类方法:
-
connect(SocketAddress endpoint)
:建立与指定的服务器地址进行连接。SocketAddress
是一个表示网络套接字地址的抽象类,可以是 IPv4 地址、IPv6 地址或 Unix 域套接字文件。 -
close()
:关闭 Socket 连接。 -
getOutputStream()
:获取与 Socket 关联的输出流,用于发送数据。 -
getInputStream()
:获取与 Socket 关联的输入流,用于接收数据。 -
sendUrgentData(int data)
:发送紧急数据。 -
setSoTimeout(int timeout)
:设置 Socket 超时时间(单位为毫秒),即在读取数据时最长等待时间。 -
setKeepAlive(boolean on)
:设置是否启用 TCP 连接的保持活动状态,默认是禁用的。 -
setTcpNoDelay(boolean on)
:设置是否开启 TCP 的 Nagle 算法,即启用或禁用 Nagle 算法,影响数据包的发送策略。 -
setReuseAddress(boolean on)
:设置是否重用 Socket 上的本地地址。 -
getInetAddress()
:获取与 Socket 连接的远程服务器的 IP 地址。 -
getLocalAddress()
:获取 Socket 绑定的本地 IP 地址。 -
getPort()
:获取与 Socket 连接的远程服务器的端口号。 -
getLocalPort()
:获取 Socket 绑定的本地端口号。
使用流程
-
创建
Socket
对象:根据客户端或服务器端的需求,使用Socket类的构造方法创建一个Socket
对象。对于客户端来说,需要通过构造方法指定服务器的主机名(或IP地址)和端口号来建立连接。
java
Socket socket = new Socket();
套接字在建立的时候,如果远程主机不可访问,这段代码就会阻塞很长时间,直到底层操作系统的限制而抛出异常。所以一般会在套接字建立后设置一个超时时间。
java
socket.setSoTimeout(10000);
- 建立连接(仅适用于客户端):客户端调用
Socket
对象的connect()
方法来与服务器建立连接。在连接建立之前,Socket
对象会尝试与服务器建立连接,如果连接成功,就可以进行后续的数据传输操作;如果连接失败,会抛出IOException
异常。
java
InetSocketAddress serverAddress = new InetSocketAddress(host,post);
socket.connection(serverAddress);
也可以直接通过 Socket
的构造方法指定服务器的主机名(或IP地址)和端口号直接与服务器建立连接:
java
Socket socket = new Socket(host, post);
- 获取输入输出流:通过
Socket
对象的getInputStream()
和getOutputStream()
,获取与Socket
相关联的输入流和输出流。输入流用于接收从服务器发送过来的数据,输出流用于向服务器发送数据。
java
// 获取输入流
InputStream inputStream = socket.getInputStream();
// 获取输出流
OutputStream outputStream = socket.getOutputStream();
- 数据传输:根据通信的需求,使用输入输出流实现与服务器的数据交换。可以使用 BuffererReader、BufferedWriter、DataInputStream、DataOutputStream 等类来包装输入输出流,使用不同的读写方法如
read()
和write()
,以便读写不同类型的数据。具体的读写方式要根据通信协议和应用需求确定。
java
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
BufferedWrite writer = new BufferedWrite(new OutputStreamWrite(outputStream));
// 向服务器发送数据
writer.write("Hello World");
writer.newLine();
writer.flush();
// 从服务器接收数据
String response readed.readLine();
必须在 OutputStream
关联的输出流上调用 flush()
方法,才能确保数据被及时地发送到客户端。
- 关闭连接:通信结束后,需要关闭
Socket
对象以释放资源。通过调用Socket
对象的close()
方法来关闭连接。
java
socket.close();
上述步骤中的各个方法调用可能会抛出异常,如 SocketException
、IOException
等,在使用 Socket
进行网络通信时,需要适当地处理这些异常,以保证程序的健壮性和稳定性。
InetAddress
InetAddress
是 Java 中用于表示 IP 地址的抽象类,可以用于获取本地主机或远程主机的 IP 地址和主机名。
常用方法
InetAddress
中常用的方法有:
-
getLocalHost()
: 获取本机 IP 地址。若找不到地址,抛出 UnknownHostException -
byte[] getAddress()
: 获取此InetAddress
对象的原始 IP 地址。 -
String getHostName()
: 获取此InetAddress
对象的主机名。 -
String getHostAddress()
: 获取此InetAddress
对象的 IP 地址字符串表示形式。 -
getAllByName(String host)
: 获取主机的所有 IP 地址,返回一个InetAddress
数组。
构造方法
因为 InetAddress
类是一个抽象类,所以不能直接使用 new
关键字来实例化它。需要使用 InetAddress
类提供的静态工厂方法来创建 InetAddress
对象:
getByName(String host)
: 根据给定的主机名或 IP 地址字符串返回相应的InetAddress
对象。若找不到指定主机的地址,抛出 UnknownHostException
java
InetAddress address = InetAddress.getByName("www.google.com");
getByAddress(byte[] addr)
: 根据 IP 地址的字节数组来创建一个InetAddress
对象。
java
byte []bytes = {127,0,0,1};
InetAddress address = InetAddress.getByAddress(bytes);
-
getByAddress(String host, byte[] addr)
: 根据主机名和 IP 地址的字节数组来创建一个InetAddress
对象。主机名可以为null
。 -
getByAddress(String host, byte[] addr, NetworkInterface nif)
: 根据主机名、IP 地址的字节数组和网络接口来创建一个InetAddress
对象。主机名可以为null
,网络接口可以为null
。
InetSocketAddress
InetSocketAddress
是 InetAddress
的补充,它在 InetAddress
的基础上增加了端口号的表示,用于更方便地处理网络连接。
InetSocketAddress
是 Java 中用于表示 IP 地址和端口号 的类,它是对 java.net.SocketAddress
类的实现。
InetSocketAddress
可以用于表示一个远程地址或者一个本地地址,它包含了 IP 地址和端口号信息。它可以被用于网络编程中的套接字操作,例如建立客户端和服务器之间的网络连接。
常用方法
InetSocketAddress
中常用的方法有:
-
InetSocketAddress(String hostname, int port)
: 根据给定的主机名和端口号创建一个InetSocketAddress
对象。 -
String getHostName()
: 获取主机名。 -
int getPort()
: 获取端口号。 -
InetAddress getAddress()
: 获取 IP 地址。 -
String toString()
: 返回完整的 IP 地址和端口号的字符串表示形式。
构造方法
- 根据指定的端口号创建一个
InetSocketAddress
对象,IP 地址为null
。
java
InetSocketAddress(int port)
- 根据指定的主机名和端口号创建一个
InetSocketAddress
对象。
java
InetSocketAddress(String hostname, int port)
- 根据指定的 IP 地址和端口号创建一个
InetSocketAddress
对象。
java
InetSocketAddress(InetAddress addr, int port)
InetAddress 与 InetSocketAddress 的区别
-
地址类型:
InetAddress
类表示 IP 地址(IPv4 或 IPv6),而InetSocketAddress
类表示 IP 地址和端口号的组合。 -
功能范围:
InetAddress
主要用于获取和操作 IP 地址,例如获取主机名、IP 地址转换等。而InetSocketAddress
主要用于在网络编程中表示远程地址或本地地址,包含了 IP 地址和端口号的信息,可以用于建立网络连接等操作。 -
使用场景:
InetAddress
一般用于获取本地主机的 IP 地址、解析远程主机的 IP 地址等,适用于客户端和服务器端的网络编程。InetSocketAddress
则多用于建立客户端和服务器之间的网络连接,明确指定远程地址和端口号。
SeverSocket
除了客户端 Socket
,Java 还提供了 ServerSocket
类(Socket
的子类)来创建服务器端 Socket
。通过 ServerSocket
,可以监听指定端口的客户端,接收来自客户端的连接请求,并创建新的 Socket
来与客户端进行通信。
构造方法
ServerSocket
类的构造方法有以下两种常见的重载形式:
- 使用指定的端口号创建一个 ServerSocket 实例,使用默认的[[#连接请求队列]]长度(通常由操作系统决定)。
java
ServerSocket(int post)
port
表示服务器要监听的端口号。
- 使用指定的端口号和连接请求队列的最大长度创建一个 ServerSocket 实例。
java
ServerSocket(int port, int backlog)
backlog
表示连接请求队列的最大长度,即允许同时等待处理的连接请求数量。
连接请求队列的长度可以在几十到几百之间,但一般不建议将其设置得过大,以免占用过多资源导致性能下降。
使用 ServerSocket
的构造方法只是创建了一个 ServerSocket
实例,并没有开始对客户端的连接进行监听。需要调用 accept()
方法来开始监听并接收客户端连接。
创建 ServerSocket
对象时,可能会抛出 IOException
异常,因此需要适当地进行异常处理。
连接请求队列
ServerSocket
的构造函数中的 backlog
参数来设置的是连接请求队列的长度。
连接请求队列是指服务器端用于存储待处理连接请求的缓冲区。当服务器接收到客户端的连接请求时,请求会首先进入连接请求队列,然后服务器逐个处理队列中的连接请求。
连接请求队列具有固定的长度,当队列已满时,新的连接请求将被丢弃或拒绝。这种情况可能发生在服务器负载很高或处理连接请求的速度不够快的情况下。
因此,合理地设置连接请求队列的长度至关重要,当服务器的负荷较高时,应该适当地增加请求队列的长度,以避免出现连接请求被拒绝的情况。
常用方法
ServerSocket
类提供了一些常用的方法用于在服务器端监听和处理客户端连接。以下是一些常见的 ServerSocket
方法:
-
Socket accept()
:接收客户端的连接请求,并返回一个新的 Socket 对象,用于与客户端进行通信。当调用accept()
时,如果没有客户端连接请求,则会阻塞等待,直到有客户端连接请求到达或发生异常。 -
InetAddress getInetAddress()
:返回一个InetAddress
对象,表示 ServerSocket 绑定的本地 IP 地址。 -
int getLocalPort()
:返回服务器套接字绑定的本地端口号。 -
void close()
:关闭服务器套接字,并释放相关资源。一旦调用了close()
,则不能再使用该ServerSocket
对象接受新的连接请求。
使用流程
- 创建一个
ServerSocket
对象。
java
int port = 8080; // 指定服务器端口
ServerSocket serverSocket = new ServerSocket(port);
- 接受连接请求:调用
ServerSocket
对象的accept()
方法,等待客户端的连接请求。
java
Socket socket = serverSocket.accept();
- 处理客户端请求:获取
Socket
对象的输入流和输出流,并通过输入流和输出流与客户端进行通信和数据交换。
java
// 获取输入流
InputStream inputStream = socket.getInputStream();
// 获取输出流
OutputStream outputStream = socket.getOutputStream();
// 读取客户端发送的数据
byte[] buffer = new byte[1024];
int len = inputStream.read(buffer);
// 处理读取的数据
String data = new String(buffer, 0, len);
System.out.println("客户端发送的数据:" + data);
// 向客户端写入数据
String message = "Hello, client!";
outputStream.write(message.getBytes());
outputStream.flush();
必须在 OutputStream
所关联的输出流上调用 flush()
方法,才能确保数据被及时地发送到客户端。
- 关闭连接和释放资源:关闭
Socket
对象和ServerSocket
对象。
java
socket.close();
serverSocket.close();
在完成通信之后,需要关闭 Socket
对象和 ServerSocket
对象。关闭 Socket
对象将断开与客户端之间的连接,关闭 ServerSocket
对象会释放服务器端口。
DatagramSocket
DatagramSocket
类是 Java 中实现 [[初识 TCP IP#UDP|UDP]] 协议的核心类。与基于 [[Boxes/Theory/Computer Networks/运输层(传输层)/TCP 协议|TCP]] 的 Socket
和 ServerSocket
类不同,DatagramSocket
类提供了无连接的通信服务,发送和接收数据报(Datagram)。由于无需建立连接,UDP 通常比 TCP 更快,但可能不如 TCP 可靠。
构造方法
DatagramSocket
类提供了几种不同的构造方法来创建 DatagramSocket
对象:
- 创建一个未绑定到任何特定本地端口的
DatagramSocket
,此构造方法会随机选择一个可用的本地端口。
java
DatagramSocket();
- 创建绑定到指定本地端口的
DatagramSocket
:
java
DatagramSocket(int localPort);
使用指定的本地端口来创建 DatagramSocket
。如果指定的端口已被占用,将会抛出 SocketException
异常。
- 创建绑定到指定本地地址和端口的
DatagramSocket
,例如,如果绑定到特定网卡上的地址,可以使用这个构造方法。
java
DatagramSocket(int localPort, InetAddress localAddress);
java
InetAddress localAddress = InetAddress.getByName("本地主机地址");
int localPort = 12345;
DatagramSocket socket = new DatagramSocket(localPort, localAddress);
- 创建绑定到指定本地地址和端口,并指定网络接口的
DatagramSocket
:
java
DatagramSocket(int localPort,
InetAddress localAddress,
NetworkInterface networkInterface);
常用方法
DatagramSocket
类提供了许多方法,用于发送和接收数据报。这里列举一些 DatagramSocket
常用的方法:
-
void send(DatagramPacket packet)
:用于发送数据报。参数packet
是要发送的数据报包含的数据和目的地信息。 -
void receive(DatagramPacket packet)
:用于接收数据报。参数packet
是用于接收数据报的包。 -
void bind(SocketAddress localAddr)
:将 DatagramSocket 绑定到指定的本地地址和端口。参数localAddr
是要绑定的本地地址和端口。 -
void close()
:关闭 DatagramSocket。 -
int getLocalPort()
:获取本地端口号。 -
InetAddress getLocalAddress()
:获取本地地址。 -
void setSoTimeout(int timeout)
:设置读取操作的超时时间(单位为毫秒)。 -
int getSoTimeout()
:获取读取操作的超时时间。
DatagramPacket
DatagramPacket
是 Java 中用于表示数据报的类,用于在网络上发送或接收数据。它包含了要发送或接收的数据以及与之相关的信息,如目标地址和端口号等。
在发送数据时,可以使用 DatagramPacket
类将数据转换为字节数组,并设置目标地址和端口号等信息,然后将数据报发送给 DatagramSocket
。
在接收数据时,DatagramPacket
类可以将接收到的数据转换为字节数组,并返回有关发送方、数据长度等信息的数据报。
DatagramPacket
不仅用于发送和接收 UDP 数据报,也可以用于发送和接收 ICMP 数据报,具体取决于底层网络协议的实现。
构造方法
- 创建一个用于接收数据的空数据报。
java
DatagramPacket(byte[] buffer, int length)
buffer
:用于存储接收到的数据的字节数组length
:接收缓冲区的长度。
- 创建一个用于发送数据的数据报。
java
DatagramPacket(byte[] buffer,
int length,
InetAddress address,
int port)
buffer
:要发送的数据的字节数组length
:要发送的数据长度address
:发送目标的地址port
:发送目标的端口号
- 创建一个用于发送数据的数据报。此构造方法与前一个相同,但还允许指定
buffer
中数据的起始偏移量offset
。
java
DatagramPacket(byte[] buffer,
int offset,
int length,
InetAddress address,
int port)
根据具体的使用情况,可以选择合适的构造方法来创建 DatagramPacket
对象。第一个构造方法适用于接收数据,而第二和第三个构造方法适用于发送数据。
常用方法
-
getData()
:返回数据报的缓存区。 -
getOffset()
:返回数据报中数据的起始偏移量。 -
getSocketAddress()
:返回数据报的源地址和端口号。 -
getAddress()
:返回数据报的源地址。 -
getPort()
:返回数据报的源端口号。 -
getLength()
:返回数据报的有效数据长度。 -
setData(byte[] buf)
:设置数据报的缓存区和数据长度。如果数据长度大于缓存区长度,则只使用缓存区中的部分数据。 -
setLength(int len)
:设置数据报的有效数据长度。 -
setSocketAddress(SocketAddress address)
:设置数据报的目标地址和端口号。 -
setAddress(InetAddress iaddr)
:设置数据报的目标地址。
使用流程
- 创建一个
DatagramSocket
对象,并将其绑定到本机的端口号,或者使用随机的未绑定端口。
java
int port = 12345;
DatagramSocket socket = new DatagramSocket(port);
- 创建一个
DatagramPacket
对象,用于发送数据报文,并调用其send()
方法发送数据报
java
byte []sendData = "Hello,server!".getBytes();
InetAddress serverAddress = InetAddress.getByName("Server_ip_Address");
int serverPort = 12345;
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
// 发送数据报
socket.send(sendPacket);
- 创建一个
DatagramPacket
对象,用于接收数据报文,并调用其receive()
方法接收数据报。
java
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
// 接收数据报
socket.receive(receivePacket);
- 处理接收到的数据报
java
String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Received message from server: " + receivedMessage);
- 关闭
DatagramSocket
,释放资源并防止内存泄漏
java
socket.close();