TCP编程和UDP编程

目录

一、TCP编程:(可靠)

1、Socket:

2、服务器端:

3、客户端:

4、Scoket流:

二、UDP编程:(不可靠)

1、服务器端:

2、客户端:


一、TCP编程:(可靠)

1、Socket:

        在开发网络应用程序的时候,会遇到Socket这个概念。Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络。SocketTCP和部分IP的功能都是由操作系统提供的,不同的编程语言只是提供了对操作系统调用的简单的封装。例如:Java提供的几个Socket相关的类就封装了操作系统提供的接口:ServerSocket类、Socket类。

        使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。因此,当Socket连接成功地在服务器端和客户端之间建立后:

  • 对服务器端来说:它的Socket是指定的IP地址和指定的端口号;
  • 对客户端来说:它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。

2、服务器端:

       要使用Socket编程,我们首先要编写服务器端程序。Java标准库提供了ServerSocket来实现对指定IP和指定端口的监听。ServerSocket的典型实现代码如下

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(6666); // 监听指定端口
        System.out.println("server is running...");
        while (true) {
            Socket sock = ss.accept();
            
            // 使用Socket流进行网络通信
            // ...
            
            System.out.println("connected from " + sock.getRemoteSocketAddress());
        }
    }
}

服务器端通过下述代码,在指定端口6666监听。这里我们没有指定IP地址,表示在计算机的所有网络接口上进行监听。

ServerSocket ss = new ServerSocket(6666);

如果ServerSocket监听成功,我们就使用一个无限循环来处理客户端的连接,注意到代码ss.accept()表示每当有新的客户端连接进来后,就返回一个Socket实例,这个Socket实例就是用来和刚连接的客户端进行通信的。

  while (true) {
      Socket sock = ss.accept();
      System.out.println("connected from " + sock.getRemoteSocketAddress());
  }

如果没有客户端连接进来,accept()方法会阻塞并一直等待。如果有多个客户端同时连接进来,ServerSocket会把连接扔到队列里,然后一个一个处理。对于Java程序而言,只需要通过循环不断调用accept()就可以获取新的连接。

3、客户端:

相比服务器端,客户端程序就要简单很多。一个典型的客户端程序如下:

public class Client {
    public static void main(String[] args) throws IOException {
        // 连接指定服务器和端口
        Socket sock = new Socket("localhost", 6666); 
        
       // 使用Socket流进行网络通信
       // ...
        
        // 关闭
        sock.close();
        System.out.println("disconnected.");
    }
}

客户端程序通过下述代码,连接到服务器端,注意上述代码的服务器地址是"localhost",表示本机地址,端口号是6666。如果连接成功,将返回一个Socket实例,用于后续通信。

Socket sock = new Socket("localhost", 6666);

4、Scoket流:

Socket连接创建成功后,无论是服务器端,还是客户端,我们都使用Socket实例进行网络通信。因为TCP是一种基于流的协议,因此,Java标准库使用InputStreamOutputStream来封装Socket的数据流,这样我们使用Socket的流,和普通IO流类似:

// 用于读取网络数据:
InputStream in = sock.getInputStream();

// 用于写入网络数据:
OutputStream out = sock.getOutputStream();

写入网络数据时,必须要调用flush()方法。如果不调用flush(),我们很可能会发现,客户端和服务器都收不到数据,这并不是Java标准库的设计问题,而是我们以流的形式写入数据的时候,并不是一写入就立刻发送到网络,而是先写入内存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,这样设计的目的是为了提高传输效率。如果缓冲区的数据很少,而我们又想强制把这些数据发送到网络,就必须调用flush()强制把缓冲区数据发送出去。

二、UDP编程:(不可靠)

TCP编程相比,UDP编程就简单得多,因为UDP没有创建连接,数据包也是一次收发一个,所以没有流的概念。在Java中使用UDP编程,仍然需要使用Socket,因为应用程序在使用UDP时必须指定网络接口(IP地址)和端口号。注意:UDP端口和TCP端口虽然都使用0~65535,但他们是两套独立的端口,即一个应用程序用TCP协议占用了端口1234,不影响另一个应用程序用UDP协议占用端口1234

1、服务器端:

DatagramSocket ds = new DatagramSocket(6666); // 监听指定端口
while (true) { // 无限循环
    // 数据缓冲区:
    byte[] buffer = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
    ds.receive(packet); // 收取一个UDP数据包
    // 收取到的数据存储在buffer中,由packet.getOffset(), packet.getLength()指定起始位置和长度
    // 将其按UTF-8编码转换为String:
    String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
    // 发送数据:
    byte[] data = "ACK".getBytes(StandardCharsets.UTF_8);
    packet.setData(data);
    ds.send(packet);
}

服务器端首先使用如下语句在指定的端口监听UDP数据包:

DatagramSocket ds = new DatagramSocket(6666);

如果没有其他应用程序占据这个端口,那么代表监听成功。为了能够反复处理数据,我们使用一个死循环来处理收到的UDP数据包:

while (true) { // 死循环

}

要接收一个UDP数据包,需要准备一个byte[]缓冲区,并通过DatagramPacket实现接收:

byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);

假设我们收取到的是一个String,那么,通过DatagramPacket返回的packet.getOffset()packet.getLength()确定数据在缓冲区的起止位置:

String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);

当服务器收到一个DatagramPacket后,通常必须立刻回复一个或多个UDP包,因为客户端地址在DatagramPacket中,每次收到的DatagramPacket可能是不同的客户端,如果不回复,客户端就收不到任何UDP包。

发送UDP包也是通过DatagramPacket实现的:

byte[] data = ...
packet.setData(data);
ds.send(packet);

2、客户端:

和服务器端相比,客户端使用UDP时,只需要直接向服务器端发送UDP包,然后接收返回的UDP包:

DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666); // 连接指定服务器和端口

// 发送:
byte[] data = "Hello".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
ds.send(packet);

// 接收:
byte[] buffer = new byte[1024];
packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
String resp = new String(packet.getData(), packet.getOffset(), packet.getLength());
ds.disconnect();

客户端打开一个DatagramSocket使用以下代码:

DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666);

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呼哧呼哧.

栓Q!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值