赞
踩
网络编程是指运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。
应用程、表示层、会话层、传输层、网络层、数据链路层、物理层。
基础层:物理层、数据链路层、网络层;
传输层:TCP-UDP协议层、Socket;
高级层:会话层、表示层、应用层。
1. TCP:TCP(Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP层位于IP层之上,应用层之下的中间层。
2. UDP:UDP(User Datagram Protocol,用户数据报协议),位于OSI模型的传输层。一个无连接的协议。提供了应用程序之间要发送的数据报。由于UDP缺乏可靠性且属于无连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。
简单来说是IP地址与端口的结合协议(RFC739);
一种地址与端口的结合描述协议;
TCP/IP协议的相关Api总称,是网络Api的集合实现;
涵盖了:Stream Socket(流)和Datagram Socket(数据报)。
Socket在网络传输中用于唯一标示两个端点之间的连接;
端点:包括IP+Port; ip+port=socket
4个要素:客户端地址、客户端端口、服务器地址、服务器端口。
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:
服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。
java.net.Socket类代表客户端和服务端用来互相沟通的套接字。客户端要获得一个Socket对象通过实例化,服务端获得一个Socket对象通过accept()方法的返回值。
方法 | 描述 |
---|---|
public Socket(String host, int port) throws UnknownHostException, IOException | 创建一个流套接字并将其连接到指定的主机上的指定端口 |
public Socket(InetAddress host, int port) throws IOException | 创建一个流套接字并将其连接到指定IP的指定端口 |
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException | 创建一个流套接字并将其连接到指定的远程主机上的指定远程端口 |
public Socket() | 创建未连接的套接字 |
方法 | 描述 |
---|---|
public void connect(SocketAddress host, int timeout) throws IOExceptio | 将此套接字连接到指定主机,并指定超时时间 |
public InetAddress getInetAddress() | 返回套接字连接的地址 |
public int getPort() | 返回此套接字连接到的远程端口 |
public int getLocalPort() | 返回此套接字连接的本地端口 |
public SocketAddress getRemoteSocketAddress() | 返回此套接字连接的端点,若无返回null |
public InputStream getInputStream() throws IOException | 返回此套接字输入流 |
public OutputStream getOutputStream() throws IOException | 返回此套接字输出流 |
public void close() throws IOException | 关闭此套接字 |
此类表示互联网协议(IP)地址。Socket编程常用方法,如下:
方法 | 描述 |
---|---|
static InetAddress getByAddress(byte[] addr) | 给定原始IP地址,返回InetAddress对象 |
static InetAddress getByAddress(String host, byte[] addr) | 根据主机名和IP地址创建InetAddress对象 |
static InetAddress getByName(String host) | 根据主机名确定主机的IP地址 |
String getHostAddress() | 返回字符串类型的IP地址 |
String getHostName() | 获取此IP地址的主机名 |
static InetAddress getLocalHost() | 返回本地主机 |
String toString() | 将IP地址转化为String |
服务器应用程序通过使用java.net.ServerSocket类以获取一个端口,侦听等待客户端连接。
方法 | 描述 |
---|---|
public ServerSocket(int port) throws IOException | 创建绑定到指定端口的服务器套接字 |
public ServerSocket(int port, int backlog) throws IOException | 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号 |
public ServerSocket(int port, int backlog, InetAddress address) throws IOException | 使用指定的端口、侦听backlog和要绑定到的本地IP地址创建服务器 |
public ServerSocket() throws IOException | 创建不绑定服务器的套接字 |
方法 | 描述 |
---|---|
public int getLocalPort() | 返回此套接字在其上侦听的端口 |
public Socket accept() throws IOException | 侦听并等待套接字的连接 |
public void setSoTimeout(int timeout) | 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 |
public void bind(SocketAddress host, int backlog) | 将ServerSocket绑定到指定的地址(ip+port) |
4.6.1 Socket服务端
Socket相关接口在java.net包中已经存在,所以这里不需要再做额外的引用。
1. SocketServer.java(Socket服务端核心)
- /**
- * socket服务端
- * @author W
- * @createDate 2022/7/14
- * @description:
- */
- @Data
- @Component
- public class SocketServer {
- final static Logger log = LoggerFactory.getLogger(SocketServer .class);
- @Value("${socket.port}")
- private Integer port;
- private boolean started;
- private ServerSocket serverSocket;
- private ExecutorService executorService = Executors.newCachedThreadPool();
-
- public void start(Integer port){
- log.info("port: {}, {}", this.port, port);
- try {
- serverSocket = new ServerSocket(port == null ? this.port : port);
- started = true;
- log.info("Socket服务已启动,占用端口: {}", serverSocket.getLocalPort());
- } catch (IOException e) {
- log.error("端口冲突,异常信息:{}", e);
- System.exit(0);
- }
-
- try {
- while (started) {
- // 接收并等待客户端连接,返回客户端套接字
- Socket socket = serverSocket.accept();
- // 维持连接-长连接
- socket.setKeepAlive(true);
- ClientSocketConnect socketConnect = new ClientSocketConnect(socket);
- Thread thread = new Thread(socketConnect);
- executorService.execute(thread);
-
- }
- } catch (IOException e) {
- e.printStackTrace();
- }finally {
- try {
- serverSocket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
2. Connection.java(连接客户端相关接口)
- /**
- * @author W
- * @createDate 2022/7/25
- * @description:
- */
- public interface Connection {
- /**
- * 发送信息
- * @param message
- */
- void sendMessage(String message);
-
- /**
- * 接收信息
- * @return
- */
- String receiveMessage();
-
- /**
- * 指定socket资源回收
- */
- void close();
-
- /**
- * 判断数据连接状态
- * @return
- */
- boolean isSocketClosed();
- }
3. ClientSocketConnect.java(自定义连接客户端Socket类)
- /**
- * @author W
- * @createDate 2022/7/14
- * @description: 自定义封装连接客户端
- */
- @Slf4j
- @Data
- public class ClientSocketConnect implements Connection,Runnable{
- private Socket socket = null;
- private BufferedReader in = null;
- private BufferedWriter out = null;
- private DataInputStream inputStream = null;
- // socket输出流
- private DataOutputStream outputStream = null;
- // 获取客户端的ip
- private String clientIp;
- // private String key;
- // private String message;
-
- public ClientSocketConnect(){
-
- }
-
- public ClientSocketConnect(Socket socket){
- this.socket = socket;
- try {
- in = new BufferedReader(new InputStreamReader(socket
- .getInputStream(), "UTF-8"));
- out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"));
- inputStream = new DataInputStream(socket.getInputStream());
- outputStream = new DataOutputStream(socket.getOutputStream());
- clientIp = socket.getInetAddress().getHostAddress();
- } catch (IOException e) {
- System.out.println("客户端连接异常");
- e.printStackTrace();
- }
- }
-
- @Override
- public void run() {
- //每5秒进行一次客户端连接,判断是否需要释放资源
- while (true){
- try {
- TimeUnit.SECONDS.sleep(5);
- if (isSocketClosed()){
- log.info("客户端已关闭,其IP为:{}", clientIp);
- //关闭对应的服务端资源
- close();
- break;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * 服务端发送消息
- * 向指定客户端发送信息
- * @param message
- */
- @Override
- public void sendMessage(String message){
- System.out.println("服务端发送消息==:"+message);
- try {
- out.write(message);
- out.newLine();
- out.flush();
- } catch (Exception e) {
- System.out.println("发送信息异常:{}");
- close();
- }
- }
-
- /**
- * 服务端接收消息
- * 获取指定客户端的上传信息
- * @return
- */
- @Override
- public String receiveMessage(){
- try {
- String msg = in.readLine();
- return msg;
- } catch (IOException e) {
- e.printStackTrace();
- close();
- }
- return null;
- }
-
- /**
- * 指定Socket资源回收
- */
- @Override
- public void close(){
- log.info("进行资源回收"+clientIp);
- IOUtils.closeQuietly(in);
- IOUtils.closeQuietly(out);
- try {
- this.socket.close();
- } catch (Exception e) {
- log.warn("close socket get excption:", e);
- }
- log.info("ClientSocketConnect----->close 资源回收成功");
- }
-
- /**
- * 发送数据包,判断数据连接状态
- * @return
- */
- @Override
- public boolean isSocketClosed(){
- try {
- //socket.sendUrgentData(1);
- inputStream = new DataInputStream(socket.getInputStream());
- outputStream = new DataOutputStream(socket.getOutputStream());
- outputStream.write("测试1".getBytes(StandardCharsets.UTF_8));
- byte[] bytes = new byte[1024];
- inputStream.read(bytes);
- String receive = new String(bytes, "utf-8");
- System.out.println("服务端接收消息===:" + receive);
- return false;
- } catch (Exception e) {
- return true;
- }
- }
- }
4.6.2 Socket客户端
Client.java(模拟客户端)
- /**
- * @author W
- * @createDate 2022/7/15
- * @description: 模拟客户端
- */
- public class Client {
- public static void main(String[] args) {
- String host = "127.0.0.1";
- Integer port = 8533 ;
- try {
- // 与服务端建立连接
- Socket socket = new Socket(host, port);
- socket.setOOBInline(true);
-
- // 建立输入输出流
- DataInputStream inputStream = new DataInputStream(socket.getInputStream());
- DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
- int i = 0;
- while (true){
- send("客户发送",outputStream);
- receive(inputStream);
- i++;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
-
- }
- /**
- * 发送数据
- * @param str
- * @param outputStream
- */
- public static void send(String str,DataOutputStream outputStream) throws Exception{
- System.out.println("客户端发送消息:=="+str);
- try {
- outputStream.write(str.getBytes());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * 接收数据
- * @param inputStream
- * @return
- * @throws Exception
- */
- public static String receive(DataInputStream inputStream) throws Exception{
- try {
- byte[] bytes = new byte[1024];
- inputStream.read(bytes);
- String info = new String(bytes,"utf-8");
- System.out.println("客户端接收信息:=="+info);
- return info;
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
4.6.3 Socket配置,在启动SpringBoot时启动Socket服务。
- @SpringBootApplication
- public class SpringbootSocketApplication {
-
- public static void main(String[] args) {
- ApplicationContext applicationContext = SpringApplication.run(SpringbootSocketStudentApplication.class, args);
- applicationContext.getBean(SocketServer.class).start(8533);
- }
-
- }
客户端通过ip和端口,连接到指定的server,然后通过Socket获得输出流,并向其输出内容,服务器会获得消息。服务端通过serverSocket.accept()侦听等待接收客户端连接,通过Socket获得输出流,向客户端发送消息,客户端通过Socket获得输入流接收消息。客户端关闭,服务端会进行相关资源回收。
客户端:
服务端:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。