Logo

郎哥编程

构建TCP服务

2021-07-27 343

学习目标:能够使用套接字技术建立TCP服务,并通过客户端连接TCP服务。

构建TCP服务

在客户端和服务器端通信模式中,可以使用ServerSocket对象来构建TCP服务端。服务端需要创建监听特定端口的ServerSocket,ServerSocket负责接收客户连接请求,并生成与客户端连接的Socket。

ServerSocket类的构造方法说明如下:

●   ServerSocket()

该构造方法创建一个未绑定的服务器套接字。

●   ServerSocket(int port)

该构造方法创建绑定到指定端口的服务器套接字。参数port为指定的端口号。套接字的默认绑定IP地址为0.0.0.0,用来表示本机任意的ipv4地址。

●   ServerSocket(int port, int backlog)

该构造方法创建服务器套接字并将其绑定到具有指定backlog的指定本地端口号。参数port为端口号,参数backlog为请求传入连接队列的最大长度。如果连接请求在队列已满时到达,则连接将被拒绝。套接字的默认绑定IP地址为0.0.0.0,用来表示本机任意的ipv4地址。

●   ServerSocket(int port, int backlog, InetAddress bindAddr)

该构造方法使用指定的端口、侦听backlog和要绑定到的指定的IP地址创建服务器。参数bindAddr为指定IP地址的InetAddress对象。

ServerSocket类的常用方法说明如下:

●   Socket  accept()

该方法用于侦听与此套接字的连接请求,并接收该请求,该方法将阻塞,直到建立连接。该方法将返回一个新的套接字。

●   InetAddress       getInetAddress()

该方法返回此服务器套接字的本地地址。如果套接字在关闭前已绑定,则此方法将在套接字关闭后继续返回本地地址。

●   void     bind(SocketAddress endpoint)

该方法将服务器套接字绑定到特定地址(IP地址和端口号)。如果地址为空,那么系统将获取一个临时端口和一个有效的本地地址来绑定套接字。参数endpoint是SocketAddress类型,指定了要绑定的IP地址和端口号。

●   boolean      isBound()

该方法返回服务器套接字的绑定状态。

●   netAddress getInetAddress()

该方法返回此服务器套接字的本地地址。

●   int       getLocalPort()

该方法返回此套接字侦听的端口号。

●   void     close()

该方法关闭该套接字。关闭该套接字后,当前在accept()塞的任何线程都将抛出SocketException。

在服务器端创建ServerSocket对象,并绑定监听端口。调用ServerSocket对象的accept()方法监听客户端的请求。与客户端建立连接后,通过输入流读取客户端发送的请求信息,然后通过输出流向客户端发送响应信息,最后关闭socket及相关资源。

案例4:编写一个TCP服务端程序。建立TcpServer类,在TcpServer类中创建套接字,侦听指定的端口,等待连接请求。

在PUnit16项目新建tcp包,在tcp包下新建TcpServer类。代码如下:

package tcp;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
 
public class TcpServer {
    // 定义ServerSocket对象
    ServerSocket serverSocket = null;
 
    // 定义端口号
    int port;
 
    // 构造方法
    public TcpServer() {
        this.port = 9099;
    }
 
    /**
     * @Title: getIp
     * @Description: 获取服务器的IP地址
     * @param @return    参数
 
     * @return String    返回类型
     * @throws
     */
    public String getIp()
    {
        if( null != serverSocket )
        {
            InetAddress address     = serverSocket.getInetAddress();
            return address.getHostAddress();
        }
        return "";
    }
 
    /**
     * @Title: init
     * @Description: 初始化和启动服务器
     * @param     参数
 
     * @return void    返回类型
     * @throws
     */
    public void init() {
        System.out.println("服务器启动...");
        try {
            /**
             * 创建一个ServerSocket,这里可以指定连接请求的队列长度 new ServerSocket(port,3)
             * 意味着当队列中有3个连接请求时,如果客户端再请求连接,就会被服务器拒绝
             */
            serverSocket = new ServerSocket(port, 3);
            // 服务器循环监听连接请求
            while (true) {
                // 从请求队列中取出一个客户端的连接请求
                Socket client = serverSocket.accept();
                // 建立一个新线程处理这次连接
                new HandlerThread(client);
            }
        } catch (Exception e) {
            System.out.println("服务器异常: " + e.getMessage());
        }
    }
 
    /**
     * @ClassName: HandlerThread
     * @Description: 客户端请求线程处理类
     * @author 编程训练营
     * @date
     *
     */
    private class HandlerThread implements Runnable {
        // 定义socket
        Socket socket;
 
        // 构造方法
        public HandlerThread(Socket socket) {
            this.socket = socket;
            // 启动线程
            new Thread(this).start();
        }
 
        /**
         * <p>
         * Title: run
         * </p>
         * <p>
         * Description:
         * </p>
         *
         * @see java.lang.Runnable#run()
         */
 
        @Override
        public void run() {
            try {
                // 读取客户端数据
                BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                // 这里要注意和客户端输出流的写方法对应,否则会抛 EOFException
                String clientInputStr = input.readLine();
                // 处理客户端数据
                System.out.println("客户端发过来的内容:" + clientInputStr);
 
                // 向客户端回复信息
                PrintStream out = new PrintStream(socket.getOutputStream());
                System.out.print("请输入:\t");
                // 发送键盘输入的一行
                String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
                out.println(s);
 
                out.close();
                input.close();
            } catch (Exception e) {
                System.out.println("服务器 run 异常: " + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (Exception e) {
                        socket = null;
                        System.out.println("服务端 finally 异常:" + e.getMessage());
                    }
                }
            }
 
        }
 
    }
 
}

编写TCP服务端程序的主要步骤如下:

(1)    实例化ServerSocket对象,创建套接字;

(2)    调用ServerSocket对象的accept()方法监听端口;

(3)    accept()方法监听到连接请求后,会返回Socket对象,accept()是堵塞方法,如果没有客户端的连接请求,方法后面的代码不会执行;

(4)    建立一个新的线程,利用I/O流与客户端进行通信,

(5)    通信完成后,关闭Socket。

在tcp包下新建TcpServerTest测试类,启动服务器。代码如下:

package tcp;
 
public class TcpServerTest {
    public static void main(String[] args) {
        // 实例化TcpServer对象
        TcpServer tcpserver = new TcpServer();
        // 启动服务
        tcpserver.init();
 
    }
}

程序TcpServerTest实例化TcpServer对象,并调用TcpServer对象的init()方法启动服务器。

连接TCP服务

TCP客户端用于连接TCP服务器,并向服务器发送请求,接收服务器返回的数据。客户端的套接字由Socket类创建。

Socket类的主要构造方法说明如下:

●   Socket(InetAddress address, int port)

该构造方法创建流套接字并将其连接到指定IP地址处的指定端口号。

Socket类的主要方法说明如下:

●   void     bind(SocketAddress bindpoint)

该方法将将套接字绑定到bindpoint指定的IP地址和端口号。

●   void     connect(SocketAddress endpoint)

该方法将此套接字连接到指定的IP地址和端口号,IP地址和端口号由endpoint指定。

●   void     connect(SocketAddress endpoint, int timeout)

该方法将此套接字连接到指定的IP地址和端口号,IP地址和端口号由endpoint指定。参数timeout为连接超时时间。

●   void     close()

该方法关闭此套接字。

案例5:编写TCP客户端程序。建立TcpClient类,连接前面课程创建的TCP服务,并输出服务器的返回数据到控制台。

在tcp包下新建TcpClient类。代码如下:

package tcp;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
 
public class TcpClient {
    // 定义一个套接字
    Socket socket;
    // 定义端口号
    int port;
    // 定义IP地址
    String ip;
 
    // 构造方法
    public TcpClient(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }
 
    /**
     * @Title: send @Description: 向TCP服务器发送请求 @param 参数
     *
     * @return void 返回类型 @throws
     */
    public void send() {
        System.out.println("客户端正在连接服务器...");
        while (true) {
            try {
                // 实例化套接字,指定IP地址和端口号
                // 套接字实例化后,会自动向服务器发送请求
                socket = new Socket(ip, port);
                // 向服务器端发送数据
                PrintStream out = new PrintStream(socket.getOutputStream());
                System.out.print("请输入: \t");
                String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
                out.println(str);
                // 读取服务器端返回的数据
                BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String ret = input.readLine();
                System.out.println("服务器端返回过来的是: " + ret);
                // 如接收到 "OK" 则断开连接
                if ("OK".equals(ret)) {
                    System.out.println("客户端将关闭连接");
                    break;
                }
 
                out.close();
                input.close();
            } catch (Exception e) {
                System.out.println("客户端异常:" + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        socket = null;
                        System.out.println("客户端 finally 异常:" + e.getMessage());
                    }
                }
            }
        }
 
    }
 
}

编写TCP客户端程序的主要步骤如下:

(1)实例化Socket对象,创建套接字。套接字的IP地址需要和服务器的IP地址一致,如果TCP服务器没有指定IP地址,而是采用默认的IP地址,客户端套接字的IP地址可以是本地地址127.0.0.1,端口号必须一致。

(2)应用I/O类向服务器端写入数据,发送请求;

(3)应用I/O类读取服务端返回的数据;

(4)通信完成后,关闭Socket。

在tcp包下新建TcpClientTest测试类。代码如下:

package tcp;
 
public class TcpClientTest {
    public static void main(String[] args) {
        // 实例化client对象
        TcpClient  client = new TcpClient("127.0.0.1",9099);
        // 向服务端发送请求
        client.send();
 
    }
}

TcpClientTest程序实例化TcpClient对象,调用TcpClient对象的send()方法向服务器端发送请求。

客户端和服务器端测试步骤:

(1)    启动TCP服务器TcpServerTest;

(2)    启动客户端程序TcpClientTest;

(3)    在客户端程序控制台窗口输入hello,向服务器端发送输入的内容;

(4)    服务器端控制台窗口回显客户端发送的内容;

(5)    在服务器端控制台窗口输入OK,服务端向客户端返回在控制台输入的内容;

(6)    客户端判断返回的内容是OK时,结束连接。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论