TCP/UDP网络编程

news2024/11/14 1:20:40

目录

一、常见的客户端服务端模型

二、Socket套接字

1、概念

2、分类

a、流套接字

b、数据报套接字 

c、原始套接字 

三、UDP数据报套接字编程 

四、TCP数据报套接字编程 


一、常见的客户端服务端模型

客户端:用户使用的程序。

服务端:给用户提供服务的程序。

整体流程:

  1. 客户端发送请求到服务端。
  2. 服务端根据客户端的的请求数据,执行相应的业务处理。
  3. 服务端返回响应:发送处理业务的处理结果。
  4. 客户端得到处理结果,展示处理结果。

二、Socket套接字

1、概念

Socket套接字是系统提供用于网络通信的技术,也是TCP/IP协议的网络通信的基本操作单元,并且基于Socket套接字的网络程序开发就是网络编程。

2、分类

a、流套接字

流套接字使用的是TCP协议(传输控制协议),TCP协议有如下特点:

  • 有连接。
  • 可靠通信。
  • 面向字节流传输。
  • 全双工。
  • 传输数据的大小不受限制。

全双工指的是通信双方可以同时发送数据进行通信,还有半双工通信:通信双方不能同时发送数据,单工通信就是只能由一方一直发送数据。

b、数据报套接字 

数据报套接字使用的是UDP协议(用户数据报协议),UDP协议有如下的特点:

  • 无连接。
  • 不可靠。
  • 面向用户数据报传输。
  • 全双工。
  • 传输数据的大小有限制,一次最多传输64K。

c、原始套接字 

原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。

三、UDP数据报套接字编程 

UDP编程具有两个核心类:

DatagramSocket:是UDP版本的Socket对象,用于接收和发送数据报。有如下三个方法:

receive:发送数据。

send:发送数据。

close:释放资源。

DatagramPacket:表示一个UDP数据报,每次发送和接收的都是DatagramPacket对象。

服务器端代码实现:

整体流程:

  1. 首先定义一个DatagramSocket对象,用于收发数据。
  2. 定义一个指定端口号的构造方法,在构造方法中完成实例化对象。
  3. 定义start方法中,循环处理请求:定义DatagramPacket对象接收请求,处理请求后,再定义一个DatagramPacket对象发送数据。
public class UDPEchoServer {
    //首先定义一个Socket对象
    private DatagramSocket datagramSocket = null;
    //构造方法:指定一个端口号
    public UDPEchoServer(int port) throws SocketException {
        datagramSocket = new DatagramSocket(port);
    }
    //启动服务器
    public void start() throws IOException {
        System.out.println("服务器已经启动!");
        while(true){
            //数据报
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
            //接收数据报
            datagramSocket.receive(requestPacket);
            //将接收到的数据报转成字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength(),"UTF-8");
            //处理数据,返回响应
            String response = process(request);
            //将响应返回给客户端
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                    requestPacket.getSocketAddress());
            datagramSocket.send(responsePacket);
            //展示
            System.out.println("[客户IP"+responsePacket.getAddress()+"客户端口"+responsePacket.getPort()+
                    "请求"+request+"响应"+response);
        }
    }
    //简单的回显服务来处理数据
    public String process(String request){
        return request;
    }

    public static void main(String[] args) throws IOException {
        UDPEchoServer udpEchoServer = new UDPEchoServer(9090);
        udpEchoServer.start();
    }
}

注意:

服务器的端口号一般是指定的,因为在客户端程序中需要指定服务器端的的端口号。

响应服务可以先别的,这里只是简单的回显服务。

客户端代码实现: 

整体流程:

  1. 首先定义一个DatagramSocket对象,用于收发数据。
  2. 定义一个构造方法指定服务器端的IP地址和端口号,并完成DatagramSocket对象实例。
  3. 定义start方法,循环发送请求,先定义DatagramPacket对象包装请求,发送到指定的服务器端,再定义一个DatagramPacket对象接收响应处理后的数据。
public class UDPEchoClient {
    //定义Socket对象
    DatagramSocket datagramSocket =null;
    private int serverPort;
    private String serverIP;
    //构造方法时传入的是服务器端的IP地址和端口号
    public UDPEchoClient(int port,String IP) throws SocketException {
        datagramSocket = new DatagramSocket();
        serverPort = port;
        serverIP = IP;
    }
    //启动客户端
    public void start() throws IOException {
        Scanner sc = new Scanner(System.in);
        while (true){
            System.out.println("发送请求>");
            String request = sc.next();
            //发送UDP请求
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIP),serverPort);
            //发送数据
            datagramSocket.send(requestPacket);
            //读取数据并进行解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
            datagramSocket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength(),"UTF-8");
            //打印响应结果
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UDPEchoClient udpEchoClient = new UDPEchoClient(2580,"127.0.0.1");
        udpEchoClient.start();
    }
}

注意:
客户端程序一般需要系统自动分配,因为你不知道此时那个端口号没有被占用,而且客户端的个数一般多于服务器端的数目。

在客户端的构造方法中需要指定服务器端的IP地址和端口号。

在启动程序时,先启动服务器端,再启动应用器端。

例如:将回显服务改变成数据+收到。 

public class UDPEChoServer2 extends UDPEchoServer {
    public UDPEChoServer2(int port) throws SocketException {
        super(port);
    }
    public String  process (String request){
        return request+"收到";

    }

    public static void main(String[] args) throws IOException {
        UDPEChoServer2 udpeChoServer2 = new UDPEChoServer2(2580);
        udpeChoServer2.start();
    }
}

 运行结果:

四、TCP数据报套接字编程 

TCP编程的核心类:

ServerSocket:是服务器端的流套接字,创建实例对象时,可以绑定指定的端口,

accept方法:用于监听指定端口,有客户端连接的话就会返回一个Socket对象,基于该Socket对象与客户端建立连接,否则就会发生阻塞等待。

Socket:客户端与服务器端建立连接后,该类对象用于通信双方进行收发数据。创建实例对象时要绑定服务器端的IP地址和端口号。

有以下常用方法:

getInetAddress:返回套接字所连接的地址。

getInputStream:返回此套接字的输入流。

getOutputStream:返回此套接字的输出流。

服务器端代码实现

整体流程:

  1. 定义一个ServerSocket对象。
  2. 定义指定端口号的构造方法。
  3. 定义start方法来启动,创建Socket对象建立连接。
  4. 定义processConnection方法循环处理请求,先得到请求进行处理之后将结果返回给客户端。
public class TCPEchoServer {
    //定义一个服务器对象
    ServerSocket serverSocket =null;
    public TCPEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    //开始
    public void start() throws IOException {
        System.out.println("服务器已经启动");
        while(true){
            //利用Socket对象来建立连接
            Socket clientSocket = serverSocket.accept();
            processConnection(clientSocket);
        }
    }

    private void processConnection(Socket clientSocket) {
        System.out.println("[" + clientSocket.getInetAddress().toString() + " "+clientSocket.getPort()+"]已经建立连接");
        try(InputStream inputStream = clientSocket.getInputStream()){
            try(OutputStream outputStream = clientSocket.getOutputStream()){
                //读取请求
                Scanner sc = new Scanner(inputStream);
                //循环处理请求
                while(true){
                    if(!sc.hasNext()){
                        System.out.println("[" + clientSocket.getInetAddress().toString() + "]已经断开连接");
                        break;
                    }
                    String request = sc.next();
                    //对请求做出响应
                    String response = process(request);
                    //将响应返回给客户端
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(response);
                    //刷新缓存区,使客户端尽快得到响应结果
                    printWriter.flush();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPEchoServer tcpEchoServer = new TCPEchoServer(9087);
        tcpEchoServer.start();
    }
}

客户端代码实现

整体流程

  1. 定义一个Socket对象。
  2. 定义一个指定服务器的IP地址和端口号的构造方法,并完成对Socket对象的实例。
  3. 定义start方法,循环发送请求,然后接收响应。
public class TCPEchoClint {
    Socket socket =null;
    public TCPEchoClint(int serverPort,String serverIP) throws IOException {
        //与指定的服务器端建立连接
        socket = new Socket(serverIP,serverPort);
    }
    //启动
    public void start(){
        System.out.println("客户端已经连接到服务器端");
        Scanner sc =new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream()) {
            try(OutputStream outputStream = socket.getOutputStream()){
                //循环发送请求
                while(true){
                    String request = sc.next();
                    //发送请求
                    PrintWriter printWriter =new PrintWriter(outputStream);
                    printWriter.println(request);
                    printWriter.flush();
                    Scanner scanner =new Scanner(inputStream);
                    String response = scanner.next();
                    System.out.println("response:"+response);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TCPEchoClint tcpEchoClint = new TCPEchoClint(9087,"127.0.0.1");
        tcpEchoClint.start();
    }

}

但是这样实现服务器端代码存在BUG, 当再启动一个客户端就连接不到服务器,为什么呢?

 

 

原因分析:在服务器端的start方法中,利用while循环来建立连接,并调用processConnection方法,但是在processConnection方法中又有一个while循环处理请求,这样只要一个客户端请求未结束,别的客户端就连接不到服务器端,这显然不符合实际。

解决方案:在start方法中创建线程,使多个客户端能并发连接到服务器端。

public void start() throws IOException {
        System.out.println("服务器已经启动");
        while(true){
            //利用Socket对象来建立连接
            Socket clientSocket = serverSocket.accept();
            Thread t = new Thread(
                    ()-> processConnection(clientSocket)
            ) ;
            t.start();
        }
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

miracl编译及使用

文章目录Windows平台编译网址 https://miracl.com/https://github.com/miracl/MIRACL Windows平台编译 源码目录下新建文件夹ms32或ms64,把/lib/ms32doit.bat或ms64doit.bat分别拷进去。 把源码include和source目录所有文件拷贝进要编译的ms32或ms64&#xff0c…

【高阶数据结构】海量数据如何处理? (位图 布隆过滤器)

🌈欢迎来到高阶数据结构专栏~~位图 & 布隆过滤器 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一起打天下嘞&#xff0…

模拟实现list / list迭代器

前言:学习C的STL,我们不仅仅要求自己能够熟练地使用各种接口,我们还必须要求自己了解一下其底层的实现方法,这样可以帮助我们写出比较高效的代码程序! ⭐在本篇文章中,list的迭代器是重点,它不…

WSL2配置网络代理

注意:本文参考自文章:WSL2配置代理,是对原文的补充,使其适用于河对岸云服务代理。 1 开启Windows代理 1.1 开启代理软件的局域网访问权限 请注意:本文的WSL2代理配置,需要Windows的代理软件已经能够正常…

HTTPS详解及HTTPS实验

目录 HTTPS 一,https在参考模型中的位置 二,什么是HTTPS 三,什么是SSL 1,SSL 协议分为两层: 2,SSL 协议提供的服务: 四,HTTPS的加密方式 1,常见的加密算法 2,…

mysql知识点

目录 1.mysql聚合函数: 2.having(用来过滤数据): HAVING 不能单独使用,必须要跟 GROUP BY 一起使用 WHERE 与 HAVING 的对比 3.升序和降序 4.等于 5.实战demo: 1.mysql聚合函数: 常用的聚…

codeforces签到题之div3

前言 第一次codeforces,发现几个问题: 1,不知道选langua&…

17正交距阵和Gram-Schmidt正交化

标准正交向量与正交矩阵 上一节介绍过的正交向量,通过一个式子进行回顾,设q是标准正交向量组中的任意向量,则 这很好地表现了标准正交向量组内各向量的性质: 不同向量之间相互垂直(正交),向量…

Ribbon 负载均衡

介绍Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就…

屏幕录制软件推荐,分享这3款,简单好用

​网络上充斥着许多各种各样的屏幕录制软件,许多有选择困难的朋友可能会充满怀疑:哪个电脑屏幕录制软件很容易使用?屏幕录制软件推荐哪个比较好?别担心,今天,小编分享这这3个简单好用的屏幕录制软件&#x…

Day10 C++STL入门基础知识七——案例1【评委打分】

路漫漫其修远兮,吾将上下而求索 文章目录1. 承接上文1. 案例描述2. 实现思路3. 亿点点分析3.1 创建选手类3.1.1 具体思路3.1.2 代码展示3.2 创建5名选手并对其姓名、平均分进行初始化3.2.1 具体思路① 创建vector容器② 创建一个creatPlayer()函数a.调用函数b. 初始…

若依框架基于@PreAuthorize注解的权限控制

目录 一、Java注解(Annotation) 1. 概述 2. Annotation通用定义 (1)interface (2)Documented (3)Target(ElementType.TYPE) (4)Retention(Ret…

IDEA插件

Lombok用注解的方式,简化了 JavaBean 的编写。注解下面介绍一下常用的几个注解:Setter 注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。Getter 使用方法同上,区别在于生…

java基于ssm电梯服务管理信息系统的设计与实现源码+数据库

基于ssm电梯服务管理信息系统的设计与实现 技术支持 开发软件:Eclipse 项目类型:Webapp 数据库:MySQL 数据库连接池:druid 框架:SSM 数据库设计软件:PowerDesigner 前端界面开发:HTML/CSS…

Maven介绍

Maven介绍1、Maven的简单介绍2、Maven的优点3、Maven的基本知识3.1、Maven如何获取Jar包3.2、Maven仓库的分类4、Idea中的maven4.1、clean4.2、validate4.3、compile4.4、test(不常用)4.5、package4.6、verify(不常用)4.7、instal…

void*传数据,是不是像在黑洞里面拯救世界?

内核代码看到这样一个函数static inline void dev_set_drvdata(struct device *dev, void *data) {dev->driver_data data; }这个函数有什么用?看里面的代码含义大概就能知道,给 driver_data这个指针赋值,之后在其他地方就可以用这个指针…

《C语言高级》(二)------ 函数与指针 篇

目录 一、函数 1.1、创建和使用函数 1.2、全局变量与局部变量 1.3、函数的参数和返回 1.4、递归调用 1.5、斐波那契数列解法其三 1.6、汉诺塔 1.7、快速排序算法 二、指针 2.1、初识指针 2.2、指针与数组 2.3、多级指针 2.4、指针数组与数组指针 2.5、指针函数与函数指…

SpringCloud入门实战(五)-集成Ribbon

一、Ribbon简介 Spring Cloud Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单地说,就是在配置文件中列出Load Balancer(简称LB)后面所有…

python爬虫学习笔记-mongodb安装基本介绍pymongo使用

MongoDB数据存储 MongoDB是一个非关系型数据库(NoSQL). 非常适合超大数据集的存储, 由 C 语言编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系…

Spring Cloud_OpenFeign服务接口调用

目录一、概述1.OpenFeign是什么2.能干嘛二、OpenFeign使用步骤1.接口注解2.新建Module3.POM4.YML5.主启动类6.业务类7.测试8.小总结三、OpenFeign超时控制1.超时设置,故意设置超时演示出错情况2.是什么3.YML中需要开启OpenFeign客户端超时控制四、OpenFeign日志打印…