【JavaEE】网络编程--Socket、UDP、TCP

1、网络编程

指网络上的不同主机,通过不同的进程,以编程的方式进行网络通信(网络数据传输)

基本概念

  • 发送端:数据的发送方进程。
  • 接收端:数据的接收方进程。

发送和接收端是相对的,一台主机可以是接收端也可以是发送端

  • 请求和响应:获取一次网络资源,涉及的两次网络数据传输

先有请求,再有响应

  • 服务端:网络数据传输场景下,提供服务的一方进程,可对外提供服务
  • 客户端:获取服务的一方进程。

2、Socket套接字

由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信基本操作单元

【JavaEE】网络编程--Socket、UDP、TCP_第1张图片 Socket通信模型

 Socket通信通过IP地址和端口号来联系,IP地址就像楼号,端口号就是门牌号,通过IP和端口的组合就可以找到这个通信的目的端口

2.1 用户数据报协议 UDP(User Datagram Protocol)

对于数据报而言,传输的数据是打包的,如:发送的数据为100byte,必须一次发送,接受的时候也必须接收100byte,并且为一次同时接收

特点:

  • 无连接
  • 不可靠传输
  • 面向数据报
  • 有接收缓冲区,无发送缓冲区
  • 一次传输最多为64K

DatagramSocket API

UDP socket

构造方法:

DatagramSocket() 创建一个UDp数据包套接字的Socket,绑定本机的任意一个端口(一般用于客户端)
DatagramSocket(int port) 创建一个UDp数据包套接字的Socket,绑定本机的一个指定端口port(一般用于服务端)

方法:

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

DatagramPacket API

这是UDP发送和接收的数据报

构造方法:

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

在构造UDP发送的数据报时需要传入的参数SocketAddress对象可以用下面的InetSocketAddrerss来创建,该对象包含了IP和端口号

方法:

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

InetSocketAddress API

InetSocketAddress ( SocketAddress 的子类 )构造方法:

InetSocketAddress(InetAddress addr, int port) 创建一个Socket地址,包含IP地址和端口号

一发一收/回显服务器

UDP服务端

public class UDPEchoServer {

    private DatagramSocket socket = null;

    //构造方法:port为该服务器绑定的端口
    public UDPEchoServer(int port) throws SocketException{
        socket = new DatagramSocket(port);
    }

    //启动服务器
    public void start() throws IOException{
        System.out.println("Server start !");
        while(true){
            //1.读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[2048],2048);
            socket.receive(requestPacket);
            //将接收到的DatagramPacket转换为字符串打印
            String request = new String(requestPacket.getData(),0, requestPacket.getLength());

            //2.根据请求计算响应
            String response = process(request);

            //3.把响应写回到客户端
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length,requestPacket.getSocketAddress());     //SocketAddress包括IP和Port
            socket.send(responsePacket);

            //4.格式化打印一个日志,记录当前的情况
            System.out.printf("[%s : %d] req:%s ; resp:%s\n",requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);
        }
    }

    public String process(String request){
        return request;      //因为是回显服务器,所以没有其他处理,直接返回
    }

    public static void main(String[] args) throws IOException {
        UDPEchoServer server = new UDPEchoServer(6666);    //指定端口号6666
        server.start();
    }
}

运行代码:显示服务端开始运行

UDP客户端

//客户端
public class UDPEchoClient {
    private DatagramSocket socket = null;    //新建socket对象
    private String severIP;        //服务器IP
    private int severPort;         //服务器端口

    //构造方法
    public UDPEchoClient(String severIP,int serverPort)throws SocketException {
        //客服端没有指定端口,让系统自动分配一个空闲的端口
        socket = new DatagramSocket();

        this.severIP = severIP;
        this.severPort = serverPort;
    }

    public void start() throws IOException{
        Scanner scanner = new Scanner(System.in);
        while(true){
            //1.获得输入的内容
            System.out.println("->");
            String request = scanner.next();

            //2.构造一个UDP请求,发送给服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length, InetAddress.getByName(this.severIP),
                    this.severPort);
            socket.send(requestPacket);

            //3.从服务器读取UDP响应数据并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[2048],2048);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());

            //4.把服务器的响应显示到控制台上
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UDPEchoClient udpEchoClient = new UDPEchoClient("127.0.0.1",6666);  //IP地址和指定端口号
        udpEchoClient.start();
    }
}

运行代码:显示输入

 输入一行字后

客户端: 显示接收与发送内容相同【JavaEE】网络编程--Socket、UDP、TCP_第2张图片

 服务端:显示收发信息【JavaEE】网络编程--Socket、UDP、TCP_第3张图片

2.2 传输控制协议TCP(Transmission Control Protocol)

TCP回显服务器

Server服务端

public class TCPEchoServer {
    private ServerSocket listenSocket = null;   //创建ServerSocket对象

    public TCPEchoServer(int port) throws IOException {
        listenSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("Server started!");

        ExecutorService pool = Executors.newCachedThreadPool();   //创建一个线程池

        while(true){
            //1.先调用accept来接收客户端的连接
            Socket clientSocket = listenSocket.accept();

            //2.处理这个连接(在线程池中)
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

        }
    }

    public void processConnection(Socket clientSocket) throws IOException {
        //格式化输出日志
        System.out.printf("[%s:%d] Server online!\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());

        //处理客户端的请求
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {     //创建输入输出流的实例
            while(true){
                //1.读取请求并解析
                Scanner scanner = new Scanner(inputStream);                   //注意这里的输入参数是输入流
                if(!scanner.hasNext()){
                    //读取完毕,断开连接
                    System.out.printf("[%s:%d] Server offline!\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }

                //2.输入请求
                String request = scanner.next();

                //3.根据请求计算响应
                String response = process(request);

                //4.把响应写回客户端
                PrintWriter printWriter = new PrintWriter(outputStream);        //参数为输出流
                printWriter.println(response);
                printWriter.flush();                        //刷新缓冲区,确保数据已经通过网卡发送出去

                //格式化打印日志
                System.out.printf("[%s:%d] req: %s;resp: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //关闭Socket
            clientSocket.close();
        }
    }

    public String process(String request){
        return request;      //回显
    }

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

Client客户端

public class TCPEchoClient {
    private Socket socket = null;

    public TCPEchoClient(String serverIP,int serverPort) throws IOException {
        socket = new Socket(serverIP,serverPort);
    }

    public void start(){
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            while(true){
                //1.控制台输入请求
                System.out.println("->");               //提示输入
                String request = scanner.next();

                //2.向服务器发送请求
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                printWriter.flush();                     //刷新缓冲区,确保数据已经通过网卡发送出去

                //3.从服务器读取响应
                Scanner responseScanner = new Scanner(inputStream);    //参数为输入流
                String response = responseScanner.next();

                //4.输出打印响应
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws IOException {
        TCPEchoClient tcpEchoClient = new TCPEchoClient("127.0.0.1",6666);
        tcpEchoClient.start();
    }
}

你可能感兴趣的:(JavaEE,网络,udp,tcp/ip)