Java网络编程-Socket编程初涉一(简易客户端-服务器)

Java网络编程-Socket编程初涉一(简易客户端-服务器)

服务器

服务器的socket程序有以下几个任务:

  • 创建ServerSocket。
  • 绑定端口。
  • 监听端口。
  • 阻塞,等待客户端连接。
  • 与客户端连接成功后,可以数据交互。

Java网络编程-Socket简介
服务器socket程序:

package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {

        final String QUIT = "QUIT";
        final int DEFAULT_PORT = 8888;
        ServerSocket serverSocket = null;

        try {
            // 绑定监听端口
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("启动服务器,监听端口"+DEFAULT_PORT);
            while(true){
                //等待客户端连接
                Socket socket = serverSocket.accept();
                System.out.println("客户端["+socket.getPort()+"]已连接");
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));

                BufferedWriter writer = new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream()));

                String msg = null;
                //读取客户端发送的消息,并且进行回复
                while ((msg = reader.readLine()) != null){
                    System.out.println("客户端["+socket.getPort()+"]:"+msg);

                    //回复客户端
                    writer.write("服务器:已收到-"+msg+"\n");
                    writer.flush();

                    // 查看客户端是否退出
                    if(QUIT.equalsIgnoreCase(msg)){
                        System.out.println("客户端["+socket.getPort()+"]已退出");
                        break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(serverSocket != null){
                try {
                    serverSocket.close();
                    System.out.println("关闭serverSocket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

创建socket、绑定端口、监听端口

创建ServerSocket、绑定端口、监听端口其实在我们的程序中,一行语句就实现了。

serverSocket = new ServerSocket(DEFAULT_PORT);

按住CTRL,进入该构造器。
源码如下:

    public ServerSocket(int port) throws IOException {
        this(port, 50, null);
    }

进入再次调用的构造器。
源码如下:

    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
        setImpl();
        if (port < 0 || port > 0xFFFF)
            throw new IllegalArgumentException(
                       "Port value out of range: " + port);
        if (backlog < 1)
          backlog = 50;
        try {
            bind(new InetSocketAddress(bindAddr, port), backlog);
        } catch(SecurityException e) {
            close();
            throw e;
        } catch(IOException e) {
            close();
            throw e;
        }
    }

进入bind方法
源码如下:

    public void bind(SocketAddress endpoint, int backlog) throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!oldImpl && isBound())
            throw new SocketException("Already bound");
        if (endpoint == null)
            endpoint = new InetSocketAddress(0);
        if (!(endpoint instanceof InetSocketAddress))
            throw new IllegalArgumentException("Unsupported address type");
        InetSocketAddress epoint = (InetSocketAddress) endpoint;
        if (epoint.isUnresolved())
            throw new SocketException("Unresolved address");
        if (backlog < 1)
          backlog = 50;
        try {
            SecurityManager security = System.getSecurityManager();
            if (security != null)
                security.checkListen(epoint.getPort());
            getImpl().bind(epoint.getAddress(), epoint.getPort());
            getImpl().listen(backlog);
            bound = true;
        } catch(SecurityException e) {
            bound = false;
            throw e;
        } catch(IOException e) {
            bound = false;
            throw e;
        }
    }

其中有两行语句如下:

getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);

很清楚了吧,程序调用了ServerSocket的构造器,创建了ServerSocket。
而该构造器间接实现了绑定端口、监听端口(上面两行语句)。

阻塞,等待客户端连接

Socket socket = serverSocket.accept();

可以自己去看看源码。
有客户端连接成功后,会生成一个socket。

BufferedReader reader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));

BufferedWriter writer = new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream()));

获取向客户端读、写的字符流。
Java IO体系的学习总结

                String msg = null;
                //读取客户端发送的消息,并且进行回复
                while ((msg = reader.readLine()) != null){
                    System.out.println("客户端["+socket.getPort()+"]:"+msg);

                    //回复客户端
                    writer.write("服务器:已收到-"+msg+"\n");
                    writer.flush();

                    // 查看客户端是否退出
                    if(QUIT.equalsIgnoreCase(msg)){
                        System.out.println("客户端["+socket.getPort()+"]已退出");
                        break;
                    }
                }

上面是服务器读取客户端发送的消息的代码,一行一行的读取消息,并且只有当客户端发送“quit”给服务器时,才表示此客户端要退出,并且在这个过程中其他客户端是不能与服务器进行连接的,因为服务器一直在while里面读取此客户端发送的数据,不过,这只是一个体验版,以后会一步一步进行升级的,毕竟学习也是一步一步学出来的。

关闭资源

先不用纠结关闭资源的正确姿势,这是课程中讲师关闭资源的方法,等基础比较好以后,我会自己去实践一下,再进行总结,之后也会去阅读源码,写相关博客,现在的任务就是把整个流程搞明白即可,不去纠结细节,一层一层来揭开Java网络编程的面纱。

serverSocket.close();

客户端

客户端的socket程序有以下几个任务:

  • 创建Socket。
  • 连接服务器。
  • 与服务器连接成功后,可以数据交互。

客户端socket程序:

package socket;

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args){

        final String QUIT = "QUIT";
        final String DEFAULT_SERVER_HOST = "127.0.0.1";
        final int DEFAULT_PORT = 8888;
        Socket socket = null;
        BufferedWriter writer = null;

        try {
            //创建socket
            socket = new Socket(DEFAULT_SERVER_HOST , DEFAULT_PORT);

            //创建IO流
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));

            writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream()));

            //等待用户输入信息
            BufferedReader consoleReader = new BufferedReader(
                    new InputStreamReader(System.in));

            while (true){
                String input = consoleReader.readLine();

                //发送消息给服务器
                writer.write(input+"\n");
                writer.flush();

                //读取服务器返回的消息
                String msg = reader.readLine();
                System.out.println(msg);

                // 查看用户是否退出
                if (QUIT.equalsIgnoreCase(input)){
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(writer != null){
                try {
                    writer.close(); // 会自动flush()
                    System.out.println("关闭socket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

创建Socket、连接服务器

socket = new Socket(DEFAULT_SERVER_HOST , DEFAULT_PORT);

进入构造器。
源码如下:

/**
     * Creates a stream socket and connects it to the specified port
     * number on the named host.
     * <p>
     * If the specified host is {@code null} it is the equivalent of
     * specifying the address as
     * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.
     * In other words, it is equivalent to specifying an address of the
     * loopback interface. </p>
     * <p>
     * If the application has specified a server socket factory, that
     * factory's {@code createSocketImpl} method is called to create
     * the actual socket implementation. Otherwise a "plain" socket is created.
     * <p>
     * If there is a security manager, its
     * {@code checkConnect} method is called
     * with the host address and {@code port}
     * as its arguments. This could result in a SecurityException.
     *
     * @param      host   the host name, or {@code null} for the loopback address.
     * @param      port   the port number.
     *
     * @exception  UnknownHostException if the IP address of
     * the host could not be determined.
     *
     * @exception  IOException  if an I/O error occurs when creating the socket.
     * @exception  SecurityException  if a security manager exists and its
     *             {@code checkConnect} method doesn't allow the operation.
     * @exception  IllegalArgumentException if the port parameter is outside
     *             the specified range of valid port values, which is between
     *             0 and 65535, inclusive.
     * @see        java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
     * @see        java.net.SocketImpl
     * @see        java.net.SocketImplFactory#createSocketImpl()
     * @see        SecurityManager#checkConnect
     */
    public Socket(String host, int port)
        throws UnknownHostException, IOException
    {
        this(host != null ? new InetSocketAddress(host, port) :
             new InetSocketAddress(InetAddress.getByName(null), port),
             (SocketAddress) null, true);
    }

先不去看细节,先看注释Creates a stream socket and connects it to the specified port number on the named host.。创建流套接字并将其连接到命名主机上的指定端口号。很显然,客户端调用Socket构造器,创建了Socket,并且连接了服务器(服务器已经运行的情况下)。

BufferedReader reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));

writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream()));

获取向服务器读、写的字符流。

//等待用户输入信息
BufferedReader consoleReader = new BufferedReader(
                    new InputStreamReader(System.in));

等待用户输入信息,并且是控制台的输入System.in

            while (true){
                String input = consoleReader.readLine();

                //发送消息给服务器
                writer.write(input+"\n");
                writer.flush();

                //读取服务器返回的消息
                String msg = reader.readLine();
                System.out.println(msg);

                // 查看用户是否退出
                if (QUIT.equalsIgnoreCase(input)){
                    break;
                }
            }

向服务器发送消息,并且接收服务器的回复,也是一行一行的读取。

关闭资源

writer.close();

在这里插入图片描述
在这里插入图片描述
到这里,我们便实现了一个简易客户端-服务器编写。
大家也自己进行测试,应该是没问题的。

如果有说错的地方,请大家不吝赐教(记得留言哦~~~~)。

发布了279 篇原创文章 · 获赞 219 · 访问量 47万+

猜你喜欢

转载自blog.csdn.net/qq_37960603/article/details/104149443
今日推荐