Java 入门指南:Java Socket 网络通信编程

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 对象的InputStreamOutputStream 来进行数据的读写操作。

构造方法

在 Java 中,Socket 类提供了多个构造方法,用于创建 Socket 对象。

  1. 用指定的主机名(或IP地址)和端口号创建一个客户端 Socket 对象。该构造方法用于与服务器建立连接。
Socket(String host, int port)
  • host:主机名
  • port:端口号
  1. 用指定的服务器地址和端口号创建一个客户端 Socket 对象。该构造方法接受一个 InetAddress 对象,表示服务器的 IP 地址。
Socket(InetAddress address, int port)
  1. 用指定的主机名(或IP地址)、端口号、本地 IP 地址和本地端口号创建一个客户端 Socket 对象。本地 IP 地址和本地端口号参数可以用于指定客户端用于通信的网络接口。
Socket(String host, int port, 
	   InetAddress localAddr, int localPort)
  1. 用指定的服务器地址、端口号、本地 IP 地址和本地端口号创建一个客户端 Socket 对象。本地 IP 地址和本地端口号参数可以用于指定客户端用于通信的网络接口。
Socket(InetAddress address, int port, 
	   InetAddress localAddr, int localPort)
  1. 创建一个未连接的、空的 Socket 对象。该构造方法常用于服务器端 Socket 的创建,通过调用 accept() 方法接受客户端的连接并创建新的 Socket 对象。
Socket()
  1. 用指定的主机名(或IP地址)和端口号创建一个 Socket 对象,并根据 stream 参数的值选择流式套接字 (TCP) 或数据报套接 (UDP)。
Socket(String host, int port, boolean stream)
  1. 使用指定的代理服务器创建一个 Socket 对象。可以使用此构造方法在连接到服务端之前与代理服务器建立连接。
Socket(Proxy proxy)
常用方法

Socket 类提供了一系列方法来实现网络通信。以下是一些常用的 Socket 类方法:

  1. connect(SocketAddress endpoint):建立与指定的服务器地址进行连接。SocketAddress 是一个表示网络套接字地址的抽象类,可以是 IPv4 地址、IPv6 地址或 Unix 域套接字文件。

  2. close():关闭 Socket 连接。

  3. getOutputStream():获取与 Socket 关联的输出流,用于发送数据。

  4. getInputStream():获取与 Socket 关联的输入流,用于接收数据。

  5. sendUrgentData(int data):发送紧急数据。

  6. setSoTimeout(int timeout):设置 Socket 超时时间(单位为毫秒),即在读取数据时最长等待时间。

  7. setKeepAlive(boolean on):设置是否启用 TCP 连接的保持活动状态,默认是禁用的。

  8. setTcpNoDelay(boolean on):设置是否开启 TCP 的 Nagle 算法,即启用或禁用 Nagle 算法,影响数据包的发送策略。

  9. setReuseAddress(boolean on):设置是否重用 Socket 上的本地地址。

  10. getInetAddress():获取与 Socket 连接的远程服务器的 IP 地址。

  11. getLocalAddress():获取 Socket 绑定的本地 IP 地址。

  12. getPort():获取与 Socket 连接的远程服务器的端口号。

  13. getLocalPort():获取 Socket 绑定的本地端口号。

使用流程
  1. 创建 Socket 对象:根据客户端或服务器端的需求,使用Socket类的构造方法创建一个 Socket 对象。

    对于客户端来说,需要通过构造方法指定服务器的主机名(或IP地址)和端口号来建立连接。

Socket socket = new Socket();

套接字在建立的时候,如果远程主机不可访问,这段代码就会阻塞很长时间,直到底层操作系统的限制而抛出异常。所以一般会在套接字建立后设置一个超时时间。

socket.setSoTimeout(10000);
  1. 建立连接(仅适用于客户端):客户端调用 Socket 对象的 connect() 方法来与服务器建立连接。在连接建立之前,Socket 对象会尝试与服务器建立连接,如果连接成功,就可以进行后续的数据传输操作;如果连接失败,会抛出 IOException 异常。
InetSocketAddress serverAddress = new InetSocketAddress(host,post);
socket.connection(serverAddress);

也可以直接通过 Socket 的构造方法指定服务器的主机名(或IP地址)和端口号直接与服务器建立连接:

Socket socket = new Socket(host, post);
  1. 获取输入输出流:通过 Socket 对象的 getInputStream()getOutputStream(),获取与 Socket 相关联的输入流和输出流。输入流用于接收从服务器发送过来的数据,输出流用于向服务器发送数据。
// 获取输入流
InputStream inputStream = socket.getInputStream();
// 获取输出流
OutputStream outputStream = socket.getOutputStream();
  1. 数据传输:根据通信的需求,使用输入输出流实现与服务器的数据交换。可以使用 BuffererReader、BufferedWriterDataInputStream、DataOutputStream 等类来包装输入输出流,使用不同的读写方法如 read()write(),以便读写不同类型的数据。具体的读写方式要根据通信协议和应用需求确定。
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() 方法,才能确保数据被及时地发送到客户端。

  1. 关闭连接:通信结束后,需要关闭 Socket 对象以释放资源。通过调用 Socket 对象的 close() 方法来关闭连接。
socket.close();

上述步骤中的各个方法调用可能会抛出异常,如 SocketExceptionIOException 等,在使用 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
InetAddress address = InetAddress.getByName("www.google.com");
  • getByAddress(byte[] addr): 根据 IP 地址的字节数组来创建一个 InetAddress 对象。
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

InetSocketAddressInetAddress 的补充,它在 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
InetSocketAddress(int port)
  • 根据指定的主机名和端口号创建一个 InetSocketAddress 对象。
InetSocketAddress(String hostname, int port)
  • 根据指定的 IP 地址和端口号创建一个 InetSocketAddress 对象。
InetSocketAddress(InetAddress addr, int port)
InetAddress 与 InetSocketAddress 的区别
  1. 地址类型:InetAddress 类表示 IP 地址(IPv4 或 IPv6),而 InetSocketAddress 类表示 IP 地址和端口号的组合。

  2. 功能范围:InetAddress 主要用于获取和操作 IP 地址,例如获取主机名、IP 地址转换等。而 InetSocketAddress 主要用于在网络编程中表示远程地址或本地地址,包含了 IP 地址和端口号的信息,可以用于建立网络连接等操作。

  3. 使用场景:InetAddress 一般用于获取本地主机的 IP 地址、解析远程主机的 IP 地址等,适用于客户端和服务器端的网络编程。InetSocketAddress 则多用于建立客户端和服务器之间的网络连接,明确指定远程地址和端口号。

SeverSocket

除了客户端 Socket,Java 还提供了 ServerSocket 类(Socket 的子类)来创建服务器端 Socket。通过 ServerSocket,可以监听指定端口的客户端,接收来自客户端的连接请求,并创建新的 Socket 来与客户端进行通信。

构造方法

ServerSocket 类的构造方法有以下两种常见的重载形式:

  1. 使用指定的端口号创建一个 ServerSocket 实例,使用默认的[[#连接请求队列]]长度(通常由操作系统决定)。
ServerSocket(int post)
  • port 表示服务器要监听的端口号。
  1. 使用指定的端口号和连接请求队列的最大长度创建一个 ServerSocket 实例。
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 对象接受新的连接请求。

使用流程
  1. 创建一个 ServerSocket 对象。
int port = 8080; // 指定服务器端口
ServerSocket serverSocket = new ServerSocket(port);
  1. 接受连接请求:调用 ServerSocket 对象的 accept() 方法,等待客户端的连接请求。
Socket socket = serverSocket.accept();
  1. 处理客户端请求:获取 Socket 对象的输入流和输出流,并通过输入流和输出流与客户端进行通信和数据交换。
// 获取输入流
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() 方法,才能确保数据被及时地发送到客户端。

  1. 关闭连接和释放资源:关闭 Socket 对象和 ServerSocket 对象。
socket.close();
serverSocket.close();

在完成通信之后,需要关闭 Socket 对象和 ServerSocket 对象。关闭 Socket 对象将断开与客户端之间的连接,关闭 ServerSocket 对象会释放服务器端口。

DatagramSocket

DatagramSocket 类是 Java 中实现 [[初识 TCP IP#UDP|UDP]] 协议的核心类。与基于 [[Boxes/Theory/Computer Networks/运输层(传输层)/TCP 协议|TCP]] 的 SocketServerSocket 类不同,DatagramSocket 类提供了无连接的通信服务,发送和接收数据报(Datagram)。由于无需建立连接,UDP 通常比 TCP 更快,但可能不如 TCP 可靠。

构造方法

DatagramSocket 类提供了几种不同的构造方法来创建 DatagramSocket 对象:

  1. 创建一个未绑定到任何特定本地端口的 DatagramSocket,此构造方法会随机选择一个可用的本地端口。
DatagramSocket();
  1. 创建绑定到指定本地端口的 DatagramSocket
DatagramSocket(int localPort);

使用指定的本地端口来创建 DatagramSocket。如果指定的端口已被占用,将会抛出 SocketException 异常。

  1. 创建绑定到指定本地地址和端口的 DatagramSocket,例如,如果绑定到特定网卡上的地址,可以使用这个构造方法。
DatagramSocket(int localPort, InetAddress localAddress);
InetAddress localAddress = InetAddress.getByName("本地主机地址");
int localPort = 12345;
DatagramSocket socket = new DatagramSocket(localPort, localAddress);
  1. 创建绑定到指定本地地址和端口,并指定网络接口的 DatagramSocket
DatagramSocket(int localPort, 
			   InetAddress localAddress, 
			   NetworkInterface networkInterface);
常用方法

DatagramSocket 类提供了许多方法,用于发送和接收数据报。这里列举一些 DatagramSocket 常用的方法:

  1. void send(DatagramPacket packet):用于发送数据报。参数 packet 是要发送的数据报包含的数据和目的地信息。

  2. void receive(DatagramPacket packet):用于接收数据报。参数 packet 是用于接收数据报的包。

  3. void bind(SocketAddress localAddr):将 DatagramSocket 绑定到指定的本地地址和端口。参数 localAddr 是要绑定的本地地址和端口。

  4. void close():关闭 DatagramSocket。

  5. int getLocalPort():获取本地端口号。

  6. InetAddress getLocalAddress():获取本地地址。

  7. void setSoTimeout(int timeout):设置读取操作的超时时间(单位为毫秒)。

  8. int getSoTimeout():获取读取操作的超时时间。

DatagramPacket

DatagramPacket 是 Java 中用于表示数据报的类,用于在网络上发送或接收数据。它包含了要发送或接收的数据以及与之相关的信息,如目标地址和端口号等。

在发送数据时,可以使用 DatagramPacket 类将数据转换为字节数组,并设置目标地址和端口号等信息,然后将数据报发送给 DatagramSocket

在接收数据时,DatagramPacket 类可以将接收到的数据转换为字节数组,并返回有关发送方、数据长度等信息的数据报。

DatagramPacket 不仅用于发送和接收 UDP 数据报,也可以用于发送和接收 ICMP 数据报,具体取决于底层网络协议的实现。

构造方法
  1. 创建一个用于接收数据的空数据报。
DatagramPacket(byte[] buffer, int length)
  • buffer:用于存储接收到的数据的字节数组
  • length:接收缓冲区的长度。
  1. 创建一个用于发送数据的数据报。
DatagramPacket(byte[] buffer, 
			   int length, 
			   InetAddress address, 
			   int port)
  • buffer:要发送的数据的字节数组
  • length:要发送的数据长度
  • address:发送目标的地址
  • port:发送目标的端口号
  1. 创建一个用于发送数据的数据报。此构造方法与前一个相同,但还允许指定 buffer 中数据的起始偏移量 offset
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):设置数据报的目标地址。

使用流程
  1. 创建一个 DatagramSocket 对象,并将其绑定到本机的端口号,或者使用随机的未绑定端口。
int port = 12345;
DatagramSocket socket = new DatagramSocket(port);
  1. 创建一个 DatagramPacket 对象,用于发送数据报文,并调用其 send() 方法发送数据报
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);
  1. 创建一个 DatagramPacket 对象,用于接收数据报文,并调用其 receive() 方法接收数据报。
byte[] receiveData = new byte[1024];

DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);

// 接收数据报
socket.receive(receivePacket);
  1. 处理接收到的数据报
String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Received message from server: " + receivedMessage);
  1. 关闭 DatagramSocket,释放资源并防止内存泄漏
socket.close();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值