网络编程套接字(Socket)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文主要记录有关网络编程的一些基础知识以及与socket相关的网络程序。


一、网络编程基础

1.网络编程

网络编程是指网络上的主机,通过不同的进程,以编程的方式实现网络通信(网络数据传输)。通过网络上不同主机,基于网络传输数据资源。

2.基本概念

(1)发送端和接收端
发送端是数据的发送方进程,即网络通信中的源主机。
接收端是数据的接收方进程,即网络通信中的目的主机。
(2)请求和响应
请求数据的发送,响应数据的请求。
(3)客户端和服务端
服务端:是提供服务的一方进程;
客户端:即是获取服务的一方进程。

二、套接字Socket

1.概念

Socket套接字是系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本单元。与Socket套接字的网络程序开发就是网络编程。

2.分类

Socket主要针对传输层协议进行划分:
(1)流套接字:使用传输层TCP(传输层控制协议)协议。
(2)数据报套接字:使用传输层UDP(用户数据报协议)协议。
(3)原始套接字:用于自定义传输层协议,用于读写内核没有处理的IP协议数据。

TCP UDP
有连接 无连接
可靠传输 不可靠传输
面向字节流 面向数据报
有接收缓冲区,也有发送缓冲区 有接收缓冲区,无发送缓冲区
大小不限 大小受限:一次最多传输64k
全双工 全双工

三、UDP数据报套接字编程

1.API

(1)DatagramSocket API
构造方法:DatagramSocket() 、DatagramSocket(int port)
方法:void receive(DatagramPacket p) :从套接字接收数据报;
void send(DatagramPacket p):从套接字发送数据报;
void close():关闭此数据报套接字。

(2)DatagramPacket API
构造方法:DatagramPacket(byte[] buf,int length):用来接收数据报;
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address):用来发送数据报。
方法:InetAddress:从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址;
int getPort():获取端口号;
byte[] getData():获取数据报中的数据。

(3)InetSocketAddress API
方法:InetSocketAddress(InetAddress addr,int port):创建一个Socket地址。

2.案例

案例1

回响服务,客户端向服务端请求数据,服务端向客户端回应其请求。
服务端
UdpEchoServer(示例):

public class UdpEchoServer {
    
    
    private DatagramSocket socket = null;
    //绑定端口号
    public UdpEchoServer(int port) throws SocketException {
    
    
        socket = new DatagramSocket(port);
    }
    //启动服务器
    public void start() throws IOException {
    
    
        System.out.println("服务器启动!");
        while (true) {
    
    
            //1.接收请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            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());
            socket.send(responsePacket);

            //4.用日志记录每一次的情况
            System.out.printf("[%s:%d]; req: %s; res: %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(9090);
        server.start();
    }
}

客户端
UdpEchoClient(示例):

public class UdpEchoClient {
    
    
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;
    public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
    
    
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
    
    
        Scanner scanner = new Scanner(System.in);
        while (true) {
    
    
            //1.读取用户输入的内容
            System.out.print("->");
            String request = scanner.next();

            //2.创建一个UDP请求,发送给服务端
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(this.serverIP),this.serverPort);
            socket.send(requestPacket);

            //3.从服务器读取UDP响应数据
            DatagramPacket responsePacket = new DatagramPacket(new byte[4090],4090);
            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 client = new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

案例2

字典客户端和字典服务端。
UdpDIctionaryServer(示例1):

public class UdpDictionaryServer {
    
    
    private DatagramSocket socket = null;
    private HashMap<String,String> dictionary = new HashMap<>();
    public UdpDictionaryServer(int port) throws SocketException {
    
    
        socket = new DatagramSocket(port);
        dictionary.put("cat","小猫");
        dictionary.put("dog","小狗");
        dictionary.put("pig","小猪");
    }
    public void start() throws IOException {
    
    
        System.out.println("服务器启动!");
        while (true) {
    
    
            //1.接收客户端请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            //将请求转为字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //2.服务器计算请求响应时间
            String response = process(request);
            //3.将响应写回客户端
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //4.打印记录的日志
            System.out.printf("[%s %d] req : %s; res : %s\n",requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);
        }
    }

    private String process(String request) {
    
    
        return dictionary.getOrDefault(request,"没有找到该词");
    }

    public static void main(String[] args) throws IOException {
    
    
        UdpDictionaryServer server = new UdpDictionaryServer(9595);
        server.start();
    }
}

UdpDictionaryServer(示例2):

public class UdpDictionaryServer extends UdpEchoServer{
    
    
    private HashMap<String,String> dic = new HashMap<>();
    public UdpDictionaryServer(int port) throws IOException {
    
    
        super(port);
        dic.put("cat","小猫");
        dic.put("dog","小狗");
        dic.put("pig","小猪");
    }

    @Override
    public String process(String request) {
    
    
        return dic.getOrDefault(request,"没有找到该词!");
    }

    public static void main(String[] args) throws IOException {
    
    
        UdpDictionaryServer server = new UdpDictionaryServer(10010);
        server.start();
    }
}

UdpDictionaryClient 与UdpEchoClient一样。

四、TCP套接字编程

1.API

(1)SeverSocket API
构造方法:ServerSocket(int port):创建一个服务端流套接字,并绑定到指定端口。
方法:Socket accept():开始监听指定端口,有客户端连接,返回一个Socket对象,并给予该Socket建立与客户端的连接,否则阻塞等待。
void close():关闭此套接字。

(2)Socket API
构造方法:Socket(String host,int port):创建一个客户端流套接字,并与对应IP的主机上,对应端口的进程建立连接。
方法:InetAddress getInetAddress():返回此套接字所连接的地址;
InputStream getInputStream():返回此套接字的输入流;
OutputStream getOutputStream():返回次套接字的输出流。

2.案例

Tcp版本的回响服务

服务端(包括:使用多线程与线程池)
TcpEchoServer(示例):

public class TcpEchoServer {
    
    
    private ServerSocket listenSocket = null;
    public TcpEchoServer(int port) throws IOException {
    
    
        listenSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
    
    
        System.out.println("服务器启动!");
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while (true) {
    
    
            //1.接受客户端请求
            Socket clientSocket = listenSocket.accept();
            //使用多线处理多个任务,由于不知道任务有多少个会浪费资源,使用线程池
            //2.建立与客户端的连接
            /*Thread t = new Thread(()->{
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            t.start();*/
            threadPool.submit(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        processConnection(clientSocket);
                    } catch (IOException e) {
    
    
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    private void processConnection(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) {
    
    
                //1.读取请求解析
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()) {
    
    
                    System.out.printf("[%s:%d] 下线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                String request = scanner.next();
                //2.根据请求计算响应
                String response = process(request);
                //3.将响应写回客户端
                PrintStream printStream = new PrintStream(outputStream);
                printStream.println(response);
                //刷新缓冲器确保数据是通过网卡发送出去的
                printStream.flush();
                //打印记录的日志
                System.out.printf("[%s:%d] req : %s; res : %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            clientSocket.close();
        }
    }

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

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

客户端
TcpEchoClient(示例):

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.print("->");
                String request = scanner.next();
                //2.发送请求给服务器
                PrintStream printStream = new PrintStream(outputStream);
                printStream.println(request);
                printStream.flush();
                //3.从服务器读取响应
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                //4.打印响应内容
                System.out.println(response);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

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

猜你喜欢

转载自blog.csdn.net/qq_45283185/article/details/127345894