网络编程(TCP/UDP)套接字



一、套接字

Socket套接字,是由系统提供用于网络通信的技术(操作系统给应用程序提供的一组API叫做Socket API),是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。

socket可以视为是应用层和传输层之间的通信桥梁
传输层的核心协议有两种:TCP,UDP;socket API也有对应的两组,由于TCP和UDP协议差别很大,因此,这两组API差别也挺大。

分类
Socket套接字主要针对传输层协议划分为如下三类:

  1. 流套接字:使用传输层TCP协议
    TCP,即Transmission Control Protocol(传输控制协议),传输层协议;

TCP的特点

  • 有连接:像打电话,得先接通,才能交互数据;
  • 可靠传输:传输过程中,发送方知道接收方有没有收到数据.(打电话就是可靠传输);
  • 面向字节流:以字节为单位进行传输.(非常类似于文件操作中的字节流);
  • 全双工:一条链路,双向通信;
  • 有接收缓冲区,也有发送缓冲区。
  • 大小不限

对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

  1. 数据报套接字:使用传输层UDP协议
    UDP,即User Datagram Protocol(用户数据报协议),传输层协议。

UDP的特点

  • 无连接:像发微信,不需要接通,直接就能发数据;
  • 不可靠传输:传输过程中,发送方不知道接收方有没有收到数据.(发微信就是不可靠传输);
  • 面向数据报:以数据报为单位进行传输(一个数据报都会明确大小)一次发送/接收必须是一个完整的数据报,不能是半个,也不能是一个半;
  • 全双工:一条链路,双向通信;
  • 有接收缓冲区,无发送缓冲区;
  • 大小受限:一次最多传输64k;

对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。

  1. 原始套接字

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

二、UDP数据报套接字编程

UDPSocket中,主要涉及到两类:DatagramSocket、DatagramPacket;

DatagramSocket API

DatagramSocket 创建了一个UDP版本的Socket对象,用于发送和接收UDP数据报,代表着操作系统中的一个socket文件,(操作系统实现的功能–>)代表着网卡硬件设备的抽象体现。

DatagramSocket 构造方法:

方法签名 方法说明
DatagramSocket() 创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(int port) 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)

DatagramSocket 方法

方法签名 方法说明
void receive(DatagramPacket p) 从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p) 从此套接字发送数据报包(不会阻塞等待,直接发送)
void close() 关闭此数据报套接字

DatagramPacket API

代表了一个UDP数据报,是UDP Socket发送和接收的数据报,每次发送/接收数据报,都是在传输一个DatagramPacket对象。

DatagramPacket 构造方法

方法签名 方法说明
DatagramPacket(byte[] buf, int length) 构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[] buf, int offset, int length,SocketAddress address) 构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号

DatagramPacket 方法

方法签名 方法说明
InetAddress getAddress() 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort() 从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData() 获取数据报中的数据

构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。

InetSocketAddress API

InetSocketAddressSocketAddress 的子类 )构造方法:

方法签名 方法说明
InetSocketAddress(InetAddress addr, int port) 创建一个Socket地址,包含IP地址和端口号

示例1:写一个简单的客户端服务程序,回显服务(EchoSever

在这里插入图片描述
构建Socket对象有很多失败的可能

  1. 端口号已经被占用,同一个主机的两个程序不能有相同的端口号(这就好比两个人不能拥有相同的电话号码);
    此处,多个进程不能绑定同一个端口号,但是一个进程可以绑定多个端口,(这就好比一个人可以拥有多个手机号),一个进程可以创建多个Socket对象,每个Socket都绑定自己的端口。
  2. 每个进程能够打开的文件个数是有上限的,如果进程之间已经打开了很多文件,就可能导致此时的Socket文件不能顺利打开;

在这里插入图片描述
这个长度不一定是1024,假设这里的UDP数据最长是1024,实际的数据可能不够1024.

在这里插入图片描述
这里的参数不再是一个空的字节数组了,response是刚才根据请求计算的得到的响应,是非空的,DatagramPacket 里面的数据就是String response的数据。

response.getBytes().length:这里拿到的是字节数组的长度(字节的个数),而response.length得到的是字符的长度。

五元组

一次通信是由5个核心信息描述的:源IP、 源端口、 目的IP、 目的端口、 协议类型。

站在客户端角度

  1. 源IP:本机IP;
  2. 源端口:系统分配的端口;
  3. 目的IP:服务器的IP;
  4. 目的端口:服务器的端口;
  5. 协议类型:TCP;

站在服务器的角度

  1. 源IP:服务器程序本机的IP;
  2. 源端口:服务器绑定的端口(此处手动指定了9090);
  3. 目的IP:包含在收到的数据报中(客户端的IP);
  4. 目的端口:包含在收到的数据报中(客户端的端口);
  5. 协议类型:UDP;

服务器

package UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
   
    //创建一个Socket实例
    private DatagramSocket socket = null;

    //port 端口号在运行程序的时候手动指定
    public UdpEchoServer(int port) throws SocketException {
   
        //需要显示的绑定一个端口号
        //端口号是用来区分一个应用程序的,主机收到网卡上数据的时候,这个数据应该给哪个程序?
       socket = new DatagramSocket(port);
    }
    //启动服务器
    public void start() throws IOException {
   
        System.out.println("启动服务器!");
        //UDP不需要建立连接,直接接收从客户端来的数据
        while (true){
   
            //1.读取客户端发来的请求
            DatagramPacket datagramPacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(datagramPacket);//为了接收数据,需要准备好datagramPacket对象,由receive来进行接收,这里的datagramPacket为输出型参数
            //将datagramPacket解析成String
            String request = new String(datagramPacket.getData(),0,datagramPacket.getLength(),"UTF-8");
            //2.根据请求计算响应(由于这里是回显服务,所以2省略)
            String response = process(request);
            //3.把响应写回到客户端
            //第一个参数不再是一个空的字节数,第三个参数:表示要把数据发给哪个端口+地址
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,datagramPacket.getSocketAddress());
            socket.send(responsePacket);
            System.out.printf("[%s:%d] req :%s,resp: %s\n",responsePacket.getAddress().toString(),
                    responsePacket.getPort(),request,response);
        }
    }

    //由于是回显服务,响应就和请求一样
    public String process(String request) {
   

        return request;
    }

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

在这里插入图片描述
这里就是系统自动给客户端分配的端口;
客户端可以有很多的,一个服务器可以给很多客户端提供服务

客户端

package UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class UdpEchoClient {
   
    
  • 14
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值