建筑网站建设公司,百度推广怎么使用教程,外贸市场推广,模版网站商城文章目录 UDP Socket APIDatagramSocketDatagramPacket例子#xff1a;UDP版本的回显服务器-客户端 TCP Socket APIServerSocketSocket例子#xff1a;TCP版本的回显服务器-客户端 UDP Socket API
DatagramSocket
这是一个 socket 类#xff0c;本质上相当于一个文件… 文章目录 UDP Socket APIDatagramSocketDatagramPacket例子UDP版本的回显服务器-客户端 TCP Socket APIServerSocketSocket例子TCP版本的回显服务器-客户端 UDP Socket API
DatagramSocket
这是一个 socket 类本质上相当于一个文件在系统中还有一种特殊的 socket 文件对应到网卡设备。
构造一个 DatagramSocket 对象就相当于打开了一个内核中的 socket 文件
构造方法
构造方法说明DatagramSocket()构造一个数据报套接字并将其绑定到本地主机上的任何可用端口DatagramSocket(int port)构造一个数据报套接字并将其绑定到本地主机上的指定端口
普通方法
方法说明void receive(DatagramPacket p)从该套接字接收数据报此方法会一直阻塞直到接收到数据报为止void send(DatagramPacket p)从此套接字发送数据报void close()关闭此数据报套接字
DatagramPacket
表示一个 UDP 数据报UDP 是面向数据报的协议传输数据就是以 DatagramPacket 为基本单位
构造方法说明DatagramPacket(byte buf[], int length)构造一个 DatagramPacket用于接收长度为 length 的数据包length 参数必须小于或等于 buf.lengthDatagramPacket(byte buf[], int length, SocketAddress address)构造一个数据报用于将长度为 length 的数据报发送到指定主机上的指定端口号。length 参数必须小于或等于 buf.lengthDatagramPacket(byte buf[], int offset, int length, SocketAddress address)构造一个数据报用于将偏移量为 ioffset 的 length 长度的数据报发送到指定主机上的指定端口号。length 参数必须小于或等于 buf.lengthDatagramPacket(byte buf[], int length, InetAddress address, int port)构造一个数据报用于将长度为 length 的数据包发送到指定主机上的指定端口号。length 参数必须小于或等于 buf.length
方法说明InetAddress getAddress()返回发送此数据报或接收数据报的机器的 IP 地址SocketAddress getSocketAddress()获取此数据包发送到或来自的远程主机的 SocketAddress通常为IP地址端口号int getPort()返回发送的数据报中的接收端主机端口号或者接收的数据报中的发送端主机端口号byte[] getData()返回数据缓冲区int getLength()返回要发送的数据长度或接收的数据长度
InetSocketAddress
创建 DatagramPacket 时需要 SocketAddress该对象通过 InetSocketAddress 创建。
构造方法说明InetSocketAddress(InetAddress addr, int port)根据 IP 地址和端口号创建套接字地址
例子UDP版本的回显服务器-客户端
服务器
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UDPEchoServer {private final DatagramSocket socket;public UDPEchoServer(int port) throws SocketException {socket new DatagramSocket(port);}public void start() throws IOException {System.out.println(服务器 启动);while (true) {// 读取客户端发来的请求DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096); // 空的用于接收响应socket.receive(requestPacket);// 对请求进行解析把 DatagramPacket 转成 StringString request new String(requestPacket.getData(), 0, requestPacket.getLength());// 处理响应String response process(request);// 把响应构造成 DatagramPacket 对象DatagramPacket responsePacket new DatagramPacket(response.getBytes(),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 server new UDPEchoServer(8000);server.start();}
}客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UDPEchoClient {private final DatagramSocket socket;public UDPEchoClient() throws SocketException {// 客户端端口一般自动分配socket new DatagramSocket();}public void start() throws IOException {Scanner scanner new Scanner(System.in);while (true) {// 客户端从控制台读取数据System.out.print( );String request scanner.next();// 构造 DatagramPacketDatagramPacket requestPacket new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(127.0.0.1), 8000);// 发送给服务器socket.send(requestPacket);// 从服务器读取响应DatagramPacket responsePacket new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);// 把响应数据转成字符串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 client new UDPEchoClient();client.start();}
}因为这里的 socket 创建出来就会一直用所以是伴随程序的整个生命周期的所以不需要手动调用 close() 去关闭 技巧Windows 使用 netstat -ano | findstr 端口号可以查找占用该端口的进程pid TCP Socket API
ServerSocket
ServerSocket 是创建 TCP 服务端 Socket 的 API
构造方法说明ServerSocket(int port)创建绑定到指定端口的服务器套接字
方法说明Socket accept()侦听要与此套接字建立的连接并接受该连接。该方法将阻塞直到建立连接为止。void close()关闭此套接字相当于发送 FIN
Socket
构造方法说明Socket(String host, int port)创建流套接字并将其连接到命名主机上的指定端口号
方法说明InetAddress getInetAddress()返回套接字所连接的地址int getPort()返回此套接字所连接的远程端口号InputStream getInputStream()返回此套接字的输入流OutputStream getOutputStream()返回此套接字的输出流
例子TCP版本的回显服务器-客户端
服务端
package network;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;public class TCPEchoServer {private final ServerSocket serverSocket;public TCPEchoServer(int port) throws IOException {serverSocket new ServerSocket(port);}public void start() throws IOException {System.out.println(服务器 启动);while (true) {Socket clientSocket serverSocket.accept();// 创建新线程去完成工作主线程继续acceptThread t new Thread(() - {try {processConnect(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});t.start();}}// 短连接一个连接只进行一次数据交互一个请求 一个响应// 长连接一个连接进行多次数据交互N 个请求 N 个响应public void processConnect(Socket clientSocket) throws IOException {System.out.printf([%s:%d] 建立连接\n, clientSocket.getInetAddress().toString(), clientSocket.getPort());try (InputStream inputStream clientSocket.getInputStream();OutputStream outputStream clientSocket.getOutputStream()) {Scanner scanner new Scanner(inputStream);PrintWriter printWriter new PrintWriter(outputStream);// 长连接while (true) {if (!scanner.hasNext()) {System.out.printf([%s:%d] 断开连接\n, clientSocket.getInetAddress().toString(), clientSocket.getPort());break;}// 读取请求并解析String request scanner.next();// 根据请求计算响应String response process(request);// 把响应写回客户端printWriter.println(response); // 注意补上空白符如换行对面next读的时候要读到空白符才往下走printWriter.flush();System.out.printf([%s:%d] req: %s, resp: %s\n, clientSocket.getInetAddress().toString(),clientSocket.getPort(), request, response);}} finally {// 一定要记得关闭 clientSocket// 因为它是 accept 创建出来的每来一个连接就会创建一个占用文件描述符资源clientSocket.close();}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TCPEchoServer server new TCPEchoServer(8000);server.start();}
}客户端
package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TCPEchoClient {private final Socket socket;public TCPEchoClient() throws IOException {socket new Socket(127.0.0.1, 8000); // 此时触发三次握手}public void start() throws IOException {Scanner scanner new Scanner(System.in);try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {Scanner scannerNet new Scanner(inputStream);PrintWriter printWriter new PrintWriter(outputStream);while (true) {// 从控制台读取用户输入System.out.print( );String request scanner.next();// 把请求发送给服务器printWriter.println(request); // 注意补上空白符如换行对面next读的时候要读到空白符才往下走printWriter.flush();// 从服务器读取响应String response scannerNet.next();System.out.printf(req: %s; resp: %s\n, request, response);}}}public static void main(String[] args) throws IOException {TCPEchoClient client new TCPEchoClient();client.start();}
}上述服务端代码还可以使用线程池改进
public void start() throws IOException {System.out.println(服务器 启动);// 使用线程池适合写自动扩容版本的ExecutorService service Executors.newCachedThreadPool();while (true) {Socket clientSocket serverSocket.accept();service.submit(() - {try {processConnect(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});}
}注意
这里的 TCP 服务器之所以使用多线程是因为处理的是长连接与客户端建立好连接之后什么时候断开连接不确定这一个连接里要处理多少请求也不确定单线程处理连接里的循环的时候就无法 accept 新的连接了。
如果是短连接每次连接只处理一个请求就可以不使用多线程了。