网络编程套接字Socket

一.什么是网络编程

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

二.为什么要实现网络编程

我们通过网络编程可以在网络中获取资源,实质是通过网络,获取到我们所需要的资源。

三.如何进行网络编程

针对网络编程,操作系统提供了一套专门实现网络编程的API,称为Socket套接字

我们的程序在应用层,操作系统工作在传输层,socket套接字就是传输层提供给应用层的API,传输层中最知名的协议就是TCP和UDP。

四.Socket关键字

Socket关键字针对传输协议分为三类:

①TCP传输协议②UDP传输协议③原始套接字

Tcp传输协议和UDP传输协议的区别:

有连接:相当于打电话,得先接通,才能交互数据

无连接:相当于发微信,不需要接通,直接就能发数据

可靠传输:传输过程中,发送方道知接收方有没有收到数据

不可靠传输:传输过程中,发送方不知道接收方有没有收到数据

面向字节流:以字节为单位进行传输

面向数据报:以数据报为单位进行传输(一个数据报都会明确大小)一次发送/接收必须是一个完整的数据报

全双工:一条链路,双向通信

半双工:一条链路,单向通信

大小限制:打电话可长可短,但是发短信有字数限制。

4.1UDP中的API

主要涉及两个类DatagramSocket(针对服务器上启动的网络服务,对应到服务器上用来接收用户请求的进程)和DatagramPacket(针对通信报文)

在网络编程中收发数据的过程主要是通过网卡来实现的,接收数据是对网卡的读取,发送数据时对网卡的写入。

构造方法:

收发和关闭方法:

notes:receive()方法会在没有客服端访问时进入阻塞状态,send()方法则是直接发送

测试要求:

根据请求返回响应:响应数据与请求数据一致

服务端代码:

import java.io.IOException;
import java.net.BindException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

/**
 * @author tongchen
 * @create 2023-02-17 23:30
 */
public class UDPEchoServer {
    //创建socket变量
    private DatagramSocket socket;
    //通过构造方法指定端口号

    public UDPEchoServer(int port) throws SocketException {
        //检查端口号的合法性
        if(port<0||port>65535){
            throw new BindException("端口建议在合法范围内");
        }
        //创建socket
        this.socket=new DatagramSocket(port);
    }
    //启动服务
    public void start() throws IOException {
        //标识运行
        System.out.println("服务端开始启用.......");
        while(true){
            //使用datagrampacket进行数据的接收
            DatagramPacket datagramPacket = new DatagramPacket(new byte[1024], 1024);
            //使用socket接收数据
            socket.receive(datagramPacket);
            //解析接收到的数据
            String request=new String(datagramPacket.getData(), 0, datagramPacket.getData().length, "utf-8");
            //根据数据获取响应数据
            String response=pocess(request);
            //对响应数据进行封装
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(StandardCharsets.UTF_8),
                    response.getBytes().length,datagramPacket.getSocketAddress());
            //发送响应数据
            socket.send(responsePacket);
            System.out.printf("[%s:%d] request = %s, response = %s\n", datagramPacket.getAddress().toString(),
                    datagramPacket.getPort(), request, response);
        }

    }

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

    public static void main(String[] args) throws IOException {
        //创建端口号
        UDPEchoServer udpEchoServer = new UDPEchoServer(9090);
        udpEchoServer.start();
    }
}

客户端代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-18 11:00
 */
public class UDPchoClient {
    //定义服务器的地址和端口号
    private String address;
    private int port;
    private DatagramSocket socket;
    //初始化端口号和目标地址
    public UDPchoClient(String address, int port) throws SocketException {
        this.address = address;
        this.port = port;
        this.socket=new DatagramSocket();
    }
    //启动程序
    public void start() throws IOException {
        System.out.println("客户端启动......");
        //输入请求数据
        while(true){
            System.out.println("请输入请求数据");
            Scanner scanner=new Scanner(System.in);
            String request=scanner.nextLine();
            //与服务端建立连接
            InetSocketAddress inetSocketAddress = new InetSocketAddress(address, port);
            //进行数据的封装
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8), request.getBytes().length, inetSocketAddress);
            //发送数据
            socket.send(requestPacket);
            //创建接收packet
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            //接收数据
            socket.receive(responsePacket);
            //解析数据
            String response=new String(responsePacket.getData(), 0, responsePacket.getLength(), "utf-8");
            //打印解析到的数据
            System.out.printf("request=%s,response=%s\n",request,response);
        }

    }

    public static void main(String[] args) throws IOException {
        //创建客户端
        UDPchoClient udPchoClient = new UDPchoClient("127.0.0.1", 9090);
        //启动客户端
        udPchoClient.start();

测试结果分析

测试要求2:请求英文单词,返回其中文翻译

服务端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

/**
 * @author tongchen
 * @create 2023-02-18 11:36
 */
public class UDPdicServer {
    //创建字典
    private HashMap<String,String>dic;
    //创建socket
   private DatagramSocket socket;
    //通过构造方法进行对所需内容的初始化

    public UDPdicServer(int port) throws SocketException {
        //创建新的hashmap
        this.dic=new HashMap<>();
        dic.put("tiger", "老虎");
        dic.put("cat", "小猫");
        dic.put("dog", "小狗");
        dic.put("pig", "小猪");
        //创建新的socket
        socket=new DatagramSocket(port);
    }

    //创建启动方法
    public void start() throws IOException {
        System.out.println("服务端启动.......");
        //创建循环
        while(true){
            //创建接收请求的packet
            DatagramPacket requestPacket=new DatagramPacket(new byte[1024],1024);
            //接收数据
            socket.receive(requestPacket);
            //对数据进行解析
            String request=new String(requestPacket.getData(), 0, requestPacket.getLength(), "utf-8");
            //根据请求获取响应
            String response=pocess(request);
            //封装数据
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(StandardCharsets.UTF_8), response.getBytes().length, requestPacket.getSocketAddress());
            //发送数据
            socket.send(responsePacket);
            //打印结果
            System.out.printf("[%s:%d] request = %s, response = %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }

    }

    private String pocess(String request) {
        return dic.getOrDefault(request, "查无此词");
    }

    public static void main(String[] args) throws IOException {
        //创建服务端
        UDPdicServer udPdicServer = new UDPdicServer(9091);
        udPdicServer.start();
    }
}

用户端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-18 11:00
 */
public class UDPchoClient {
    //定义服务器的地址和端口号
    private String address;
    private int port;
    private DatagramSocket socket;
    //初始化端口号和目标地址
    public UDPchoClient(String address, int port) throws SocketException {
        this.address = address;
        this.port = port;
        this.socket=new DatagramSocket();
    }
    //启动程序
    public void start() throws IOException {
        System.out.println("客户端启动......");
        //输入请求数据
        while(true){
            System.out.println("请输入请求数据");
            Scanner scanner=new Scanner(System.in);
            String request=scanner.nextLine();
            //与服务端建立连接
            InetSocketAddress inetSocketAddress = new InetSocketAddress(address, port);
            //进行数据的封装
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8), request.getBytes().length, inetSocketAddress);
            //发送数据
            socket.send(requestPacket);
            //创建接收packet
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            //接收数据
            socket.receive(responsePacket);
            //解析数据
            String response=new String(responsePacket.getData(), 0, responsePacket.getLength(), "utf-8");
            //打印解析到的数据
            System.out.printf("request=%s,response=%s\n",request,response);
        }

    }

    public static void main(String[] args) throws IOException {
        //创建客户端
        UDPchoClient udPchoClient = new UDPchoClient("127.0.0.1", 9091);
        //启动客户端
        udPchoClient.start();
    }
}

结果分析:

4.2实现TCP协议的类

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket 构造方法:

Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端

Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据

的。

Socket 构造方法:

我们通过实例对TCP协议进行讲解:客户端向服务端发送数据,服务端返回同样的数据

服务端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-20 10:50
 */
public class TcpEchoService {
    //通过创建构造方法指定端口号获取socket连接
    private ServerSocket socket;

    public TcpEchoService(int port) throws IOException {
        //判断端口的有效性
        if(port<1025||port>65535){
            throw new BindException("端口建议在1025和65535之间.......");
        }
        //通过指定端口号创建socket服务
        this.socket=new ServerSocket(port);
    }

    //获取客户端连接
    public void start() throws IOException {
        System.out.println("服务端端获取连接成功......");
        //不断循环接收用户的请求
        while(true){
            //获取与客户端的连接
            Socket clientSocket = socket.accept();
           //处理用户请求
            pocessRequset(clientSocket);

        }

    }

    private void pocessRequset(Socket clientSocket) throws IOException {
        //打印客户端信息
        System.out.printf("%s|%d,客服端连接成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //通过clientSocket中获取输入和输出数据
        try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){
            //循环从输入中获取数据
            while (true){
                Scanner scan=new Scanner(inputStream);
                //判断是否存在
                if(!scan.hasNextLine()){
                    System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //获取数据并获得返回数据
                String request=scan.nextLine();
                String response=pocess(request);
                //发送数据
                PrintWriter printWriter = new PrintWriter(outputStream);
                //注意输入输出格式的一致性(用户和客户端)
                printWriter.println(response);
                //强制刷新缓存区
                printWriter.flush();
                //打印信息
                System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭资源
            clientSocket.close();

        }
    }

    private String pocess(String request) {
        return request;
    }
    //初始化服务器
    public static void main(String[] args) throws IOException {
        TcpEchoService tcpEchoService = new TcpEchoService(9090);
        tcpEchoService.start();
    }

}

用户端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-20 11:17
 */
public class TcpEchoClient {
    //创建socket
    private Socket socket;
    //构造方法创建socket

    public TcpEchoClient(String address,int port) throws IOException {
        this.socket=new Socket(address, port);
    }
    //连接
    public void start(){
        System.out.printf("客户端启动成功....");
        try(InputStream inputStream=socket.getInputStream(); OutputStream outputStream=socket.getOutputStream()){
            //循环输入
            while(true){
                System.out.println("->");
                Scanner scan=new Scanner (System.in);
                String request=scan.nextLine();
                //验证request的有效性
                if(request==null||request.equals("")){
                    System.out.println("输入的请求不能为空");
                    continue;
                }
                //将request输入到socket
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                //强制刷新
                printWriter.flush();
                //在socket中获取请求
                Scanner scanner = new Scanner(inputStream);
                String response = scanner.nextLine();
                System.out.printf("request = %s, response = %s\n", request, response);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {

        }
        //循环输入

    }
    //验证‘=
    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);
        tcpEchoClient.start();
    }
}

一个客户端时可以正常运行,建立两个客户端时却出现了下面的情况:

我们去分析情况,是因为只有一个线程,客户端没有退出,服务端卡在第二个while循环中出不来

解决措施也很简单,开启多线程即可,为了线程频繁的开启和销毁所消耗的系统资源,我们使用线程池进行优化:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author tongchen
 * @create 2023-02-20 12:18
 */
public class ThreadPoolTest {
    //通过创建构造方法指定端口号获取socket连接
    private ServerSocket socket;

    public ThreadPoolTest(int port) throws IOException {
        //判断端口的有效性
        if(port<1025||port>65535){
            throw new BindException("端口建议在1025和65535之间.......");
        }
        //通过指定端口号创建socket服务
        this.socket=new ServerSocket(port);
    }

    //获取客户端连接
    public void start() throws IOException {
        System.out.println("服务端端获取连接成功......");
        //不断循环接收用户的请求
        //创建线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10));
        while(true){
            //获取与客户端的连接
            Socket clientSocket = socket.accept();
            threadPoolExecutor.submit(()->{
                try {
                    //处理用户请求
                    pocessRequset(clientSocket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            });

        }

    }

    private void pocessRequset(Socket clientSocket) throws IOException {
        //打印客户端信息
        System.out.printf("%s|%d,客服端连接成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //通过clientSocket中获取输入和输出数据
        try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){
            //循环从输入中获取数据
            while (true){
                Scanner scan=new Scanner(inputStream);
                //判断是否存在
                if(!scan.hasNextLine()){
                    System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //获取数据并获得返回数据
                String request=scan.nextLine();
                String response=pocess(request);
                //发送数据
                PrintWriter printWriter = new PrintWriter(outputStream);
                //注意输入输出格式的一致性(用户和客户端)
                printWriter.println(response);
                //强制刷新缓存区
                printWriter.flush();
                //打印信息
                System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭资源
            clientSocket.close();

        }
    }

    private String pocess(String request) {
        return request;
    }
    //初始化服务器
    public static void main(String[] args) throws IOException {
        ThreadPoolTest tcpEchoService = new ThreadPoolTest(9090);
        tcpEchoService.start();
    }
}

第二个任务:实现简单对话:

import javax.sound.sampled.Port;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author tongchen
 * @create 2023-02-20 15:01
 */
public class TcpMsgServer {
    //创建socket
private ServerSocket socket;
    public TcpMsgServer(int port) throws IOException {
        this.socket=new ServerSocket(port);
    }
    //开启工作
    public void start() throws IOException {
        System.out.println("用户端开启成功......");
        //循环接收用户请求
        //创建线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10));
        while(true){
            //接收用户请求
            Socket clientSocket = socket.accept();
            //处理用户请求(使用多线程)
            threadPoolExecutor.submit(()->{
                try {
                    pocessRequset(clientSocket) ;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    private void pocessRequset(Socket clientSocket) throws IOException {
        System.out.printf("%s|%d,客服端连接成功......\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //获取两个流
        try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){
            //多次获取用户请求
            while(true){
                //获取用户输入
                Scanner scan=new Scanner(inputStream);
                if(!scan.hasNext()){
                    System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                String request= scan.nextLine();
                //根据请求获取响应
                String response=pocess(request);
                //通过print发回
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                //打印效果
                printWriter.flush();
                System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            clientSocket.close();
        }
    }

    private String pocess(String request) {
        System.out.println("请求为"+request);
        System.out.println("请输入你的响应->");
        Scanner scanner=new Scanner(System.in);
        String response=scanner.nextLine();
        return response;
    }
    //开启
    public static void main(String[] args) throws IOException {
        TcpMsgServer msgServer = new TcpMsgServer(9090);
        msgServer.start();
    }
}

猜你喜欢

转载自blog.csdn.net/m0_65431718/article/details/129070781