Socket programming of network programming

But to make the Dragon City Flying generals, only because they are in this mountain

Before Introduction to BIO and NIO (Netty leader) In this article, we talked about socket. This article wants to broaden our horizons and talk about socket from the perspective of computer network

Review the old and know the new, and talk about the network model

The above figure shows the OSI (open system interconnection reference model) seven layer network reference model of ISO (International Standardization Organization). This model is a standard and a reference, and now the most widely used implementation is the four layer model of TCP/IP.

Tips: here, there are four layers of TCP/IP model and five layers on the Internet. The difference is that the four layers combine the data link layer and the physical layer into one network interface layer. Neither of these two statements is wrong.

Talk about something real

I feel that what the picture says is still a little empty. I understand it, but I don't seem to fully understand it. Let's talk about the real TCP/IP network model we are really using. Take five layers as an example, which is clearer.

Generally speaking, most of our daily programming plays are in the application layer and transport layer. OK, Socket programming is mentioned here. Where is the Socket? For what? Keep it real

Talk about Socket

The so-called Socket programming is actually connecting the application layer and the transport layer in series to meet our needs. It mainly operates the transport layer protocol, and our mainstream transport layer protocols are TCP and UDP, so we can focus on these two protocols.

I have to say TCP and UDP

A question often asked: what is the difference between TCP and UDP? Usually the answer is this

TCP is connection oriented; UDP is for connectionless.

TCP is reliable, error free, loss free and sequence guaranteed; UDP is unreliable and not guaranteed

TCP is byte stream oriented and is a stream when sending; UDP is datagram oriented and sent one by one

TCP can provide flow control and congestion control; UDP no

The above is probably a common answer, but for their own understanding, it seems that there is always some confusion in it. such as

What is the connection? Is it true that such an abstract link is maintained in the network cable?

No, "connection" means a path. In fact, it only exists at both ends, that is, the two ends that need communication.

In fact, establishing a connection is to establish a data structure at both ends of the communication, and then maintain this data structure to ensure that both sides of the communication can identify the information sent by each other, and use this data structure to ensure the connection oriented characteristics.

Something real: connect at both ends, not the path. It can be understood as the coordination of data structures at both ends. Compared with Java, this data structure can be understood as Class

If the data structures on both sides are in the same state and comply with the rules of TCP, the connection is considered to exist. If they are not matched, the connection is broken.

The so-called reliable, sequential and streaming transmission are also guaranteed by the data structures at both ends. Reliability is that the data structure is called, the order is that the data structure is sorted, and the streaming transmission is that the data structure is merged and sent uniformly to a single packet.

Examples of life:

I think it's very vivid that we send and receive express when we buy things. You and the merchant are at both ends. The network layer is equivalent to that you know the address of each other and send the package to the logistics. You don't know exactly what happened. You only care about the results, The "data structure" is the behavior correction made by the merchant and the buyer for the abnormal package, which is used to ensure that the express arrives correctly and intact.

Back to Socket programming

Through the above foreshadowing, we can know that Socket programming is actually end-to-end programming, which controls the logic on the end. Sometimes we will hear other related terms. Such as Socket file and Socket handle. In fact, on our most commonly used Linux server, Socket exists in the form of a file, so there is also a limit on the size of memory and the number of Socket connections, otherwise your server will break down sooner or later.

The following figure shows the pseudo code of Socket for the Java version of TCP protocol. Other languages have similar logic.

Here, the server binds the port, calls the accept method, waits for a connection request, completes three TCP handshakes, and returns the Socket object of the link after success. If you need to handle multiple connections, you need to call the accept method many times, so you can often see that the accept method is set in a loop.

It can be found that the client does not seem to need to bind ports when creating Socket connections. This is because the system will randomly assign a port for connection. Because the client is only used by ourselves, we do not care about the number of client ports.

Implementing RPC remote service call with Socket

ok, based on theory, here I simply implement an RPC interface in Java to provide you with a practical reference.

Server unified interface

public interface RpcServer {

    void stop();

    void start() throws IOException;

    void register(Class<?> serviceInterface, Class<?> impl);

    boolean isRunning();

    int getPort();

}

Server default implementation

public class DefaultServer implements RpcServer {

    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();

    private static boolean isRunning = false;

    private int port;

    public DefaultServer(int port) {
        this.port = port;
    }

    @Override
    public void stop() {
        isRunning = false;
        executor.shutdown();
    }

    @Override
    public void start() throws IOException {
        System.out.println("start server");
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(port));
        System.out.println("====Registered service====");
        for (Map.Entry<String, Class> entry : serviceRegistry.entrySet()) {
            System.out.println("Registered service name:"+entry.getKey()+" realization:"+entry.getValue());
        }
        System.out.println("==========");
        try {
            while (true) {
                executor.execute(new ServerTask(serverSocket.accept()));
            }
        } finally {
            serverSocket.close();
        }


    }

    @Override
    public void register(Class<?> serviceInterface, Class<?> impl) {
        serviceRegistry.put(serviceInterface.getName(), impl);
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    @Override
    public int getPort() {
        return port;
    }

    private class ServerTask implements Runnable {

        private Socket socket;

        public ServerTask(Socket socket) {
            this.socket = socket;
        }


        @Override
        public void run() {
            ObjectInputStream inputStream = null;
            ObjectOutputStream outputStream = null;
            try {
                inputStream = new ObjectInputStream(socket.getInputStream());
                String serviceName = inputStream.readUTF();
                String methodName = inputStream.readUTF();
                Class<?>[] parameterTypes = (Class<?>[]) inputStream.readObject();
                Object[] arguments = (Object[]) inputStream.readObject();
                Class<?> serviceClass = serviceRegistry.get(serviceName);
                if (serviceClass == null) {
                    throw new ClassNotFoundException(serviceName + " not found");
                }
                Method method = serviceClass.getMethod(methodName, parameterTypes);
                Object result = method.invoke(serviceClass.newInstance(), arguments);
                outputStream = new ObjectOutputStream(socket.getOutputStream());
                outputStream.writeObject(result);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }
}

Client implementation

public class RPCClient<T> {

    public static  <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) {

        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class[]{serviceInterface},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket = null;
                        ObjectOutputStream output = null;
                        ObjectInputStream input = null;
                        try {
                            socket = new Socket();
                            socket.connect(addr);

                            output = new ObjectOutputStream(socket.getOutputStream());
                            output.writeUTF(serviceInterface.getName());
                            output.writeUTF(method.getName());
                            output.writeObject(method.getParameterTypes());
                            output.writeObject(args);

                            // 4. Synchronous blocking, wait for the server to return the response, and return after obtaining the response
                            input = new ObjectInputStream(socket.getInputStream());
                            return input.readObject();


                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            if (socket != null) socket.close();
                            if (output != null) output.close();
                            if (input != null) input.close();
                        }
                        return null;
                    }
                });
    }

}

demo class

public interface Demo {
    String getName(String name);
}
public class DemoAImpl implements Demo{
    @Override
    public String getName(String name) {
        return "AAAAA "+name;
    }
}

Provision of services

public class ProviderTest {
    public static void main(String[] args) throws IOException {
        RpcServer rpcServer = new DefaultServer(8088);
        rpcServer.register(Demo.class, DemoAImpl.class);
        rpcServer.start();
    }
}

Client call

public class RpcTest {

    public static void main(String[] args) {
        Demo demo = RPCClient.getRemoteProxyObj(Demo.class, new InetSocketAddress("localhost", 8088));
        System.out.println(demo.getName("nice"));
    }
}

Explain

This programming method is actually very primitive and has great room for improvement. For example, NIO or Netty can be optimized Introduction to BIO and NIO (Netty leader) , there are also ways of data transmission and reasons for space. There is a pit buried here. Let's continue to talk next time.

Thousands of turns, here is dying stranded.

Keywords: socket

Added by inutero on Sun, 27 Feb 2022 13:57:40 +0200