udp/tcp回显网络编程

发布于:2024-05-06 ⋅ 阅读:(20) ⋅ 点赞:(0)
udp
DatagramSocket 

用于接收和发送udp数据报


构造方法:

  • DatagramSocket():创建一个UDP数据报套接字的Socket,绑定到本地上 一个随机可用端口上,一般用于客户端
  • DatagramSocket(int port):创建一个UDP数据报套接字的Socket,绑定到指定端口上,一般适用于服务器端

DatagramSocket方法:

  • void receive(DatagramPacket p):接收套接字数据报p,如果此时还没有发送,便会阻塞
  • void send(DatagramPacket p):发送套接字数据报p,不会阻塞,直接发送
  • void close():关闭此数据报套接字
DatagramPacket:

udp发送和接收的数据报形式


构造方法:

  • DatagramPacket(byte[] buf,int length):构造一个DatagramPacket对象来接收发送来到数据报,将数据内容放在第一个字节数组中,第二个参数是指定接收内容的长度。(用于接收时)
  • DatagramPacket(byte[] buf,int offset,int length,SocketAddress address):接收发来的数据报,并将内容放进buf数组中,选中需要截取内容的索引[offset,length)。address:指定目的主机的ip和端口号。(用于发送时)

DatagramPacket方法:

  • getSocketAddress():获取发送端的ip地址。
  • getPort():获取发送端的端口
  • byte[] getData():获取数据报中的内容

udp回显服务器实现:
服务器端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class udpServer {
    //服务器端
    private DatagramSocket socket = null;

    public udpServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        //System.out.println(socket.getPort());
        //returns the port to which the socket is connected  未连接套接字时 返回-1
        //如果想得到服务器端的端口 要使用socket.getLocalPort()
        System.out.println("服务器启动");
        //构建一个DatagramPacket来接收客户端发来的消息
        while(true){
            //创建一个DatagramPacket填充对象,用来接收从客户端发送来的数据报
            DatagramPacket reqPacket = new DatagramPacket(new byte[4096],4096);

            //如果没有数据报发送,receive发送会阻塞等待
            socket.receive(reqPacket);

            //转换成我们看得懂的字符串形式 ----- 客户端发送来的请求
            String request = new String(reqPacket.getData(),0,reqPacket.getLength());

            //服务器端做出响应
            String res = RESPONSE(request);

            //构建一个DatagramPacket将响应后的结果发送回客户端
            //注意需要有客户端socketAddress
            DatagramPacket respPacket = new DatagramPacket(res.getBytes(StandardCharsets.UTF_8),0,res.getBytes().length,
                    reqPacket.getSocketAddress());

            //发送数据报
            socket.send(respPacket);
            System.out.println("服务器端口:"+socket.getLocalPort()+": 客户端[ip:"+respPacket.getAddress()+" 端口:"+reqPacket.getPort()
                                +"] req:"+request+" res:"+res);
        }
    }
    public String RESPONSE(String req){
        return req;
    }

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

值得注意的是:DatagramSocket的getPort/getAddress方法,返回的都是连接方的端口和ip,并不是本地的。而DatagreamPacket的getPort/getAddress方法,返回的都是本地的端口和ip地址。如果你还没有连接成功时,打印socket.getPort(),虽然这时候你已经给出了指定端口,但这个方法的返回值是连接端的,还没连接时返回-1


客户端
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class udpClient {
    //目的端ip
    private String severIp;
    //目的端端口
    private int severPort;
    private DatagramSocket socket;

    public udpClient(String severIp,int severPort) throws SocketException {
        socket = new DatagramSocket();
        this.severIp = severIp;
        this.severPort = severPort;
    }

    public void start() throws IOException {
        Scanner sc = new Scanner(System.in);
        System.out.println("客户端启动");
        while(true){
            System.out.print(">>");
            String request = sc.nextLine();
            if(request.equals("exit")){
                System.out.println("退出");
                socket.close();
                return;
            }
            //给出消息发出的目的端ip和端口 (服务器ip端口)
            DatagramPacket reqPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8),request.getBytes().length
            ,InetAddress.getByName(severIp),severPort);

            //发送
            socket.send(reqPacket);

            //接收响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);

            //转换成字符串形式
            String res = new String(responsePacket.getData(),0, responsePacket.getLength());
            //打印响应
            System.out.println(res);
        }
    }

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

注意:当传服务器端的ip地址时,此时我们这里给的是一个字符串,但参数接收的是一个32位整数形式,所以我们需使用InetAddress.getName(String hostage)做一下转换


tcp
SeverSocket 

服务器端Socket API


构造方法:

  • SeverSccket(int port):创建一个服务器端流套接字Socket,并绑定到指定端口

SeverSocket的方法:

  • Socket accept():监听指定端口(创建时绑定的端口),如果有客户端连接后,返回一个服务端Socket对象,否则阻塞等待
  • void close():关闭套接字
Socket

客户端Socket,或服务器端的accept方法收到有客户端连接后返回的服务端Socket

无论是客户端还是服务器端,都是连接建立后,保存对端的信息


构造方法:

Socket(String severIp,int severPort):创建一个客户端Socket,与对应的主机端口建立连接


Socket的方法:

  • InetAddress():返回套接字所连接的地址
  • InputStream getInputStream():
  • OutputStream getOutStream():

ps:tcp是面向字节流传输的


tcp回显服务器实现

服务器端
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;

public class tcpSever {
    private ServerSocket socket;

    public tcpSever(int port) throws IOException {
        socket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        //与服务器建立连接 返回一个服务器端Socket
        Socket clickClient = socket.accept();

        processConnection(clickClient);
    }
    public void processConnection(Socket clientSocket){
        System.out.println("客户端上线[ip:"+clientSocket.getInetAddress().toString()+"  port:"+clientSocket.getPort()+"]");
        try(InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream()) {
            while(true){
                //读取结果
                //输入流 会等待客户端输入东西 阻塞在这里 (通过Scanner的方法都会造成阻塞)
                Scanner sc = new Scanner(inputStream);
                if(!sc.hasNext()){
                    //没有数据了 断开连接
                    System.out.println("客户端下线[ip:"+clientSocket.getInetAddress()+"  port:"+clientSocket.getPort());
                    socket.close();
                    break;
                }
                //遇到换行/空白结束
                String req = sc.next();

                //服务器响应
                String resp = process(req);

                //OutputStream没有写String的方法
                //1.将其转换成字节数组
                //2.使用PrintWriter-----打印流-----字符打印流/字节打印流
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(resp);
                printWriter.flush();
                System.out.println("客户端:[req:"+req+" resp:"+resp+" ip:"+clientSocket.getInetAddress()+" port:"+clientSocket.getPort()+"]");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public String process(String req){
        return req;
    }

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

客户端
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class udpClient {
    //目的端ip
    private String severIp;
    //目的端端口
    private int severPort;
    private DatagramSocket socket;

    public udpClient(String severIp,int severPort) throws SocketException {
        socket = new DatagramSocket();
        this.severIp = severIp;
        this.severPort = severPort;
    }

    public void start() throws IOException {
        Scanner sc = new Scanner(System.in);
        System.out.println("客户端启动");
        while(true){
            System.out.print(">>");
            String request = sc.nextLine();
            if(request.equals("exit")){
                System.out.println("退出");
                socket.close();
                return;
            }
            //给出消息发出的目的端ip和端口 (服务器ip端口)
            DatagramPacket reqPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8),request.getBytes().length
            ,InetAddress.getByName(severIp),severPort);

            //发送
            socket.send(reqPacket);

            //接收响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);

            //转换成字符串形式
            String res = new String(responsePacket.getData(),0, responsePacket.getLength());
            //打印响应
            System.out.println(res);
        }
    }

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

注意:

  1. 在tcp的Socket中,ip地址可以直接传字符串类型的,不需要像udpSocket那样转成32位整数形式
  2. 我们读通过next()读,写通过println写。这是设计好了的。使换行符变成我们的消息隔断符,使我们知道每一段消息的头和尾,解决粘包问题
  3. 在运行tcp回显服务器时,必须先开服务器端,再开客户端因为如果先开客户端的话,此时没有服务器与之相连,会抛异常------对应的即为tcp的是有连接的