如何学习 Java 中的 Socket 编程,进行网络通信

Socket编程是网络编程的核心技术之一,它使得不同主机之间可以进行数据通信。Java提供了丰富的网络编程API,使得编写网络应用程序变得相对简单和直观。本文将详细讲解如何学习Java中的Socket编程,并通过示例代码展示如何实现网络通信。

一、Socket编程基础

1.1 什么是Socket?

Socket是一种网络通信的端点,包含IP地址和端口号。在网络通信中,一个Socket实例代表一个网络连接的一个端点,通过两个Socket实例(客户端和服务器)可以实现网络通信。

1.2 TCP和UDP

Socket编程主要分为两种类型:基于TCP(Transmission Control Protocol)的Socket编程和基于UDP(User Datagram Protocol)的Socket编程。

  • TCP:面向连接的协议,提供可靠的、顺序正确的、无差错的数据传输。常用于需要高可靠性的场景。
  • UDP:无连接的协议,提供不保证可靠性的数据传输。适用于对传输速度要求较高,但对可靠性要求较低的场景。

二、Java中的Socket类

2.1 Java Socket API

Java提供了以下主要的Socket编程类:

  • java.net.Socket:实现客户端Socket,负责与服务器建立连接并进行通信。
  • java.net.ServerSocket:实现服务器Socket,负责监听客户端的连接请求。
  • java.net.DatagramSocket:实现UDP协议的Socket,用于发送和接收数据报文。
  • java.net.DatagramPacket:表示UDP的数据报文。

三、基于TCP的Socket编程

3.1 客户端Socket编程

客户端通过java.net.Socket类实现。步骤如下:

  1. 创建Socket对象,并连接到服务器。
  2. 获取输入输出流,用于发送和接收数据。
  3. 关闭Socket连接。

示例代码:

import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) {
        String serverName = "localhost"; // 服务器地址
        int port = 8080; // 服务器端口

        try {
            // 连接到服务器
            Socket client = new Socket(serverName, port);

            // 获取输出流,发送数据到服务器
            OutputStream outToServer = client.getOutputStream();
            DataOutputStream out = new DataOutputStream(outToServer);
            out.writeUTF("Hello from " + client.getLocalSocketAddress());

            // 获取输入流,接收服务器返回的数据
            InputStream inFromServer = client.getInputStream();
            DataInputStream in = new DataInputStream(inFromServer);
            System.out.println("Server says " + in.readUTF());

            // 关闭连接
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
3.2 服务器Socket编程

服务器通过java.net.ServerSocket类实现。步骤如下:

  1. 创建ServerSocket对象,并绑定到指定端口。
  2. 调用accept()方法,等待客户端连接。
  3. 获取输入输出流,用于接收和发送数据。
  4. 关闭Socket连接。

示例代码:

import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) {
        int port = 8080; // 监听端口

        try {
            // 创建服务器Socket
            ServerSocket serverSocket = new ServerSocket(port);

            while (true) {
                // 等待客户端连接
                Socket server = serverSocket.accept();

                // 获取输入流,接收客户端发送的数据
                DataInputStream in = new DataInputStream(server.getInputStream());
                System.out.println("Client says " + in.readUTF());

                // 获取输出流,发送数据到客户端
                DataOutputStream out = new DataOutputStream(server.getOutputStream());
                out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress());

                // 关闭连接
                server.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、基于UDP的Socket编程

4.1 客户端Socket编程

客户端通过java.net.DatagramSocketjava.net.DatagramPacket类实现。步骤如下:

  1. 创建DatagramSocket对象。
  2. 创建DatagramPacket对象,封装要发送的数据。
  3. 调用send()方法发送数据。
  4. 调用receive()方法接收数据。
  5. 关闭DatagramSocket。

示例代码:

import java.net.*;

public class UDPClient {
    public static void main(String[] args) {
        String serverName = "localhost"; // 服务器地址
        int port = 8080; // 服务器端口

        try {
            // 创建客户端Socket
            DatagramSocket clientSocket = new DatagramSocket();

            // 发送数据
            byte[] sendData = "Hello from client".getBytes();
            InetAddress IPAddress = InetAddress.getByName(serverName);
            DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port);
            clientSocket.send(sendPacket);

            // 接收数据
            byte[] receiveData = new byte[1024];
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            clientSocket.receive(receivePacket);
            String modifiedSentence = new String(receivePacket.getData());
            System.out.println("FROM SERVER: " + modifiedSentence);

            // 关闭连接
            clientSocket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
4.2 服务器Socket编程

服务器通过java.net.DatagramSocketjava.net.DatagramPacket类实现。步骤如下:

  1. 创建DatagramSocket对象,并绑定到指定端口。
  2. 创建DatagramPacket对象,用于接收数据。
  3. 调用receive()方法接收数据。
  4. 创建DatagramPacket对象,封装要发送的数据。
  5. 调用send()方法发送数据。
  6. 关闭DatagramSocket。

示例代码:

import java.net.*;

public class UDPServer {
    public static void main(String[] args) {
        int port = 8080; // 监听端口

        try {
            // 创建服务器Socket
            DatagramSocket serverSocket = new DatagramSocket(port);

            while (true) {
                // 接收数据
                byte[] receiveData = new byte[1024];
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                serverSocket.receive(receivePacket);
                String sentence = new String(receivePacket.getData());
                System.out.println("RECEIVED: " + sentence);

                // 发送数据
                InetAddress IPAddress = receivePacket.getAddress();
                int clientPort = receivePacket.getPort();
                String capitalizedSentence = sentence.toUpperCase();
                byte[] sendData = capitalizedSentence.getBytes();
                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, clientPort);
                serverSocket.send(sendPacket);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

五、Java Socket编程中的常见问题

5.1 端口冲突

端口冲突是指多个程序尝试绑定到同一个端口,这会导致端口不可用。解决方法是确保每个程序使用不同的端口,或者检查端口是否被占用。

5.2 数据传输中的阻塞

Socket通信是阻塞式的,意味着读写操作会阻塞线程直到数据被完全传输。可以使用多线程或者非阻塞I/O(NIO)来解决这个问题。

5.3 数据粘包与拆包

在TCP协议中,由于数据是以流的形式发送,接收方可能会一次接收到多个数据包(粘包)或一个数据包被分成多次接收(拆包)。解决方法是在数据包中添加长度字段,或者使用定界符来标识每个数据包的边界。

六、高级话题:多线程和NIO

6.1 多线程Socket编程

在实际应用中,服务器通常需要同时处理多个客户端的连接。可以使用多线程来实现,每个客户端连接由一个线程处理。

示例代码:

import java.io.*;
import java.net.*;

public class MultiThreadedTCPServer {
    public static void main(String[] args) {
        int port = 8080; // 监听端口

        try {
            // 创建服务器Socket
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("Server started on port " + port);

            while (true) {
                // 等待客户端连接
                Socket clientSocket = serverSocket.accept();
                System.out.println("New client connected: " + clientSocket.getInetAddress());

                // 创建一个新的线程处理客户端请求
                new ClientHandler(clientSocket).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class ClientHandler extends Thread {
    private Socket clientSocket;

    public ClientHandler(Socket socket) {
        this.clientSocket = socket;
    }

    public void run() {
        try {
            // 获取输入流,接收客户端发送的数据
            DataInputStream in = new DataInputStream(clientSocket.getInputStream());
            System.out.println("Client says: " + in.readUTF());

            // 获取输出流,发送数据到客户端
            DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());
            out.writeUTF("Thank you for connecting!");

            // 关闭连接
            clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
6.2 非阻塞I/O(NIO)

Java NIO(New I/O)提供了非阻塞的网络通信方式,可以提高服务器的性能和扩展性。NIO中的核心组件包括Channel、Buffer和Selector。

示例代码:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NioTCPServer {
    public static void main(String[] args) {
        int port = 8080; // 监听端口

        try {
            // 创建选择器
            Selector selector = Selector.open();

            // 打开服务器Socket通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("Server started on port " + port);

            while (true) {
                // 等待事件
                selector.select();

                // 获取发生事件的SelectionKey
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();

                    if (key.isAcceptable()) {
                        // 接受连接
                        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = serverChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        // 读取数据
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(256);
                        socketChannel.read(buffer);
                        String message = new String(buffer.array()).trim();
                        System.out.println("Client says: " + message);

                        // 响应客户端
                        buffer.flip();
                        socketChannel.write(buffer);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

黑马程序员免费预约咨询

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/723191.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

船舶能源新纪元:智能管理引领绿色航运潮流

在蓝色的大海上&#xff0c;无数船只乘风破浪&#xff0c;为全球的贸易和文化交流贡献着力量。然而&#xff0c;随着环保意识的提升和可持续发展的要求&#xff0c;船舶的能源消耗和排放问题逐渐成为了人们关注的焦点。在这个关键时刻&#xff0c;船舶能源管理系统应运而生&…

智能合约开发的过程

智能合约是一种运行在区块链上的程序&#xff0c;可以自动执行预先设定的条款和条件。智能合约具有去中心化、透明、不可篡改等特点&#xff0c;因此被广泛应用于金融、供应链、物联网等领域。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流…

西门子学习笔记12 - BYTE-REAL互相转化

这是针对于前面MQTT协议的接收和发送数组只能是BYTE数组做出的对应的功能块封装。 1、BYTE-REAL转化 1、把byte数组转成字符串形式 2、把字符串转成浮点数 2、REAL-BYTE转化 1、把浮点数转成字符串 2、把字符串转成Byte数组

通过编辑器工具改变Inspector锁定状态

是在看一系列视频的时候&#xff0c;看到的&#xff0c;感觉挺有用&#xff0c;就记录下来。 就是这个小锁的按钮&#xff0c;后续可以通过快捷键&#xff0c;快速锁定和解锁。代码里没有加入快捷键控制&#xff0c;有需要用到的可以自己加一下&#xff0c;比较简单 using Uni…

前端上传minio

参考【GitCode - 全球开发者的开源社区,开源代码托管平台】 注意事项&#xff1a;nodejs服务&#xff0c;文件扩展名为mjs&#xff0c;版本号8.0&#xff0c;引入的时候 import * as Minio from minio import Minio as * from minio// 实例化对象存储服务的MinIO客户端 // p…

c语言字符串函数详解(全)

字符串函数 1.strlen函数 求字符串长度的 //求字符串长度 int my_strlen(char* str) {int count 0;while (*str ! \0){count;*str;}return count; } int main() {char arr[] "abcdef";int red my_strlen(arr);printf("%d\n", red);return 0; } 2. st…

【MySQL】(基础篇十二) —— 子查询

分组数据 本文介绍什么是子查询以及如何使用它们。 SQL允许我们创建子查询&#xff08;subquery&#xff09;&#xff0c;即嵌套在其他查询中的查询。这样可以实现更复杂的查询&#xff0c;理解这个概念的最好方法是考察几个例子。 利用子查询进行过滤 需求&#xff1a;查询…

一行超长日志引发的 “血案” - Containerd 频繁 OOM 背后的真相

案发现场&#xff1a;混沌初现 2024年6月10日&#xff0c;本应是平静的一天。但从上午 9 点开始&#xff0c;Sealos 公有云的运维监控告警就开始不停地响。北京可用区服务器节点突然出现大量 “not ready” 告警&#xff0c;紧接着&#xff0c;系统自动触发 004 节点重启&…

重学java 75.JDK新特性 ① Lambda表达式

你所做的事情&#xff0c;也许暂时看不到成果&#xff0c;你不是没有成长&#xff0c;而是在扎根 —— 24.6.19 一、函数式编程思想和Lambda表达式定义格式 1.面向对象思想:是Java的核心编程思想 强调的是找对象,帮我们做事儿 比如:去北京 -> 强调的是怎么去,火车,高铁,飞机…

为什么人们对即将推出的 Go 1.23 迭代器感到愤怒

原文&#xff1a;gingerBill - 2024.06.17 TL;DR 它让 Go 变得太“函数式”&#xff0c;而不再是不折不扣的命令式语言。 最近&#xff0c;我在 Twitter 上看到一篇帖子&#xff0c;展示了 Go 1.23&#xff08;2024 年 8 月&#xff09;即将推出的 Go 迭代器设计。据我所知&a…

35 Debian如何配置Postfix+Dovecot

作者:网络傅老师 特别提示:未经作者允许,不得转载任何内容。违者必究! Debian如何配置Postfix+Dovecot 《傅老师Debian知识库系列之35》——原创 ==前言== 傅老师Debian知识库特点: 1、拆解Debian实用技能; 2、所有操作在VMware虚拟机实测完成; 3、致力于最终形成Deb…

网安人必备!开源网络安全工具TOP 10(附下载地址)

工欲善其事&#xff0c;必先利其器。对于广大的网络安全从业者&#xff0c;以及未来想要从事网络安全的人来说&#xff0c;选择并善用合适的网络安全工具&#xff0c;能有效提升工作效率。 开源网络安全工具之所以能够在众多安全解决方案中脱颖而出&#xff0c;不仅是因为它们…

1.22 LeetCode总结(基本算法)_位运算

进制的概念 进制即进位计数制&#xff0c;是利用固定的数字符号和统一的规则的带进位的计数方法。 任何一种进位计数制都有一个基数&#xff0c;基数为 X 的进位计数制称为 X 进制&#xff0c;表示每一个数位上的数运算时都是逢 X 进一。 504. 七进制数 手法1&#xff1a;当…

小林图解系统-二.硬件结构 2.4CPU缓存一致性

CPU Cache的数据写入 CPU和内存的访问性能越差越大&#xff0c;于是在CPU内部嵌入CPU Cache(高速缓存)。 CPU Cache由Cache Line组成&#xff0c;Cache Line由头标志Tag数据块Data Block组成。 如果数据写入Cache&#xff0c;内存和Cache相对应的数据将不同&#xff0c;需要…

排序(3)【归并排序】【计数排序】【排序算法度及其稳定性分析】

一.归并排序 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有…

CCAA质量管理【学习笔记】​​ 备考知识点笔记(六)质量改进系统方法与工具

第七节 质量改进系统方法与工具 1 质 量 改 进 方 法 概 述 可以说几乎每种质量管理领域的方法与工具都可以用于质量改进&#xff0c;但是一个组织在改进的整体推进中&#xff0c;往往不是采用单一的方法&#xff0c;会涉及多种改进的工具和手段&#xff0c;并依据一定的模式…

虹科免拆诊断案例 | 2022款问界M5增程式混合动力车充电口盖指示灯不工作

故障现象 一辆2022款问界M5增程式混合动力车&#xff0c;搭载1.5T发动机和发电机作为增程器&#xff0c;累计行驶里程约为3.6万km。该车因尾部受到碰撞进厂维修&#xff0c;维修后进行慢充&#xff0c;发现充电口盖指示灯不点亮&#xff08;图1&#xff09;&#xff0c;但仪表…

开放式运动耳机哪个品牌音质好用又实惠?五款2024高口碑产品精选力荐!

​开放式运动耳机在如今社会中已经迅速成为大家购买耳机的新趋势&#xff0c;深受喜欢听歌和热爱运动的人群欢迎。当大家谈到佩戴的稳固性时&#xff0c;开放式耳机都会收到一致好评。对于热爱运动的人士而言&#xff0c;高品质的开放式运动耳机无疑是理想之选。特别是在近年来…

要颜值有颜值,有性价比有性价比,华硕天选键、鼠组合分享

作为ROG产品的忠实粉丝&#xff0c;用过不少ROG 相关的产品&#xff0c;近期华硕天选TX98和天选MINI 鼠标的发布&#xff0c;独特配色令我眼前一亮。 华硕天选TX98键盘&#xff0c;作为新品&#xff0c;从看上的第一眼就觉得这款键盘是非常值得推荐。 它完美地诠释了潮玩新次元…

联想618收官:以69亿销售额勇夺15冠王

随着6月19日零点钟声的响起&#xff0c;今年的618年中大促正式落下帷幕。在这个备受瞩目的购物狂欢节里&#xff0c;联想凭借出色的产品表现和市场策略&#xff0c;再次展现了其强大的品牌实力和市场号召力。 数据显示&#xff0c;在今年618活动期间&#xff0c;联想全网销售额…