小白实战Socket网络编程一:使用Socket进行TCP连接

***前言-本系列的Socket网络编程,从小白的角度切入理解,旨在于记录从简单的Socket 实现TCP连接开始逐步到最终完成大文件消息分片模型发送的整个过程,其中整理主要包括以下8个实战记录主题:

  1. Socket实现TCP连接
  2. UDP局域网搜索连接
  3. UDP辅助TCP实现点对点传输实例
  4. 简单的聊天室实战案例
  5. NIO优化服务端线程模型
  6. 数据传输的稳定性优化
  7. 客户端发送文件到服务器实战
  8. 分片消息模型实现大文件传输实战

什么是网络编程?我们为什么需要网络编程?

  在日常的工作中,作为一个金融类清算系统的程序员,我工作的首要任务是在与产品经理交流的过程中把复杂的金融业务需求进行DDD建模,并且以此设计数据库、代码函数、代码流程等从而实现清晰稳定的业务功能;再进一步则是在实现的功能稳定之后对性能进行测试优化。
  然而某一天,一个在心里感兴趣已久的问题一直在困扰我。那些微信的聊天、视频直播、甚至是网游这类数据需要频繁发送接收的产品系统是如何实现的呢? 这就打开了我对网络编程甚至是高并发的网络编程的兴趣。

  网络编程,总体来说就是:

  • 需要对信息的发送与接收。
  • 通过操作相应的API调度计算机资源,并利用传输管道(网线)进行数据交互的过程。
  • 相关的概念: 套接字、网络模型、数据包…

Java 是如何实现网络编程的?

  到了这里,Socket这个Java耳熟能详的组件便可以浮出来了。我看过一些Java的入门视频总会直接丢出一个Socket写上一大堆代码实现数据交互,却没有告诉我们这些小白,用socket 就是在做调度计算机的资源从而进行数据收发这一概念。
  再进一步理解,对于一个微信的功能,最基本的功能便是聊天(字符串),也就是是我在一台计算机上发送的字符串出去,同时接收对方的字符串数据,结合上java实现那就可以理解成,我用java调用socket组件,把需要发送的字符串数据通过socket 调用计算机的api 通过传输管道发送到对方计算机的网卡上,另外一台计算机上利用socket调度api操作获取网卡接收到的数据,展示在需要展示的地方。

Socket 实现简单TCP连接实例:

前面的一大堆废话都是为了理清对使用这门技术的目的,那么开始实现简单的Socket TCP实例。

功能描述: 假设有A(Server)、B(Client)机器需要实现数据交互,现在以A作为服务端、B作为客户端。

  1. 实现A启动服务端监听客户端连接,B连接到A,并且发送数据到服务端,服务端接收到数据后打印在控制台然后立刻回送客户端发送过来的数据;直到客户端发送bye字符串才退出客户端连接。
  2. 服务端是允许多客户端连接的。

–Server.java

    public static void main(String[] args) throws IOException {
     
        // 启动ServerSocket 并且制定端口8888
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("[服务端启动] ServerAddress:" + serverSocket.getInetAddress() + " Port:" + serverSocket.getLocalPort());

        for(;;){
     
            // 循环 阻塞监听连接
            Socket client = serverSocket.accept();

            // 获得连接 开启线程处理连接 不阻塞主线程
            new ClientHandler(client).start();
        }
    }

服务单通过ServerSocket监听8888端口来监听客户端连接。并且使用死循环来允许接入多客户端连接,每有一个连接就创建一个ClientHandler线程对象来异步处理连接读取回送数据。让我们再来看看ClientHandler线程主方法体run()的实现:

            public void run() {
     
                System.out.println("[获得客户端连接] IP:" + client.getInetAddress().getHostAddress() + " PORT:" + client.getPort());

                try {
     
                    do {
     

                        String message = bufferedReader.readLine();
                        if(message.equalsIgnoreCase("bye")){
     
                            // 退出
                            printStream.println("bye");
                            isClosed = true;
                        }else{
     
                            System.out.println("[收到消息] 客户端IP: " + client.getInetAddress().getHostAddress() + "P:"+client.getPort()+ "的消息:" + message);
                            // 回送给客户端消息
                            printStream.println(message);
                        }
                    }while(!isClosed);

                    bufferedReader.close();
                    printStream.close();

                }catch (Exception e){
     
                    e.printStackTrace();
                    System.out.println("[异常关闭]");
                } finally {
     

                    try {
     
                        client.close();
                    } catch (IOException ex) {
     
                        ex.printStackTrace();
                    }
                }

                System.out.println("[客户端退出] IP: " + client.getInetAddress() + " P:" + client.getPort());

            }

在run方法中,会从客户端连接的socket对象中拿到输入和输出流,使用输入流读取客户端发送的数据,只要不是bye就打印并且通过输出流回送给客户端,如果是bye则结束线程的读写while循环,关闭线程工作。

–Client.java:
客户端的职责很简单,构建Socket对象,拿到输入输出流,发送数据、获得回送,打印在控制台。发送的数据是从控制台通过键盘输入的。

    public static void main(String[] args) throws IOException {
     

        // 构建客户端连接socket对象
        Socket client = new Socket();
        // 设置连接超时时间
        client.setSoTimeout(3000);

        // 绑定连接服务端的ip 端口
        client.connect(new InetSocketAddress("127.0.0.1",8888));

        requestAndResponse(client);

        client.close();
    }

–requestAndResponse方法主体:

            do{
     
                // 阻塞获得键盘输入的消息
                String typeInMsg = bufferedReader.readLine();
                // 发送消息到服务端
                output.println(typeInMsg);

                //从服务端接收一条消息
                String receiveMsg = input.readLine();
                if(!receiveMsg.equalsIgnoreCase("bye")){
     
                    System.out.println("[服务端回送消息]: " + receiveMsg);
                }else{
     
                    flag = true;
                }
            }while(!flag);

  通过键盘输入流构建的bufferReader,从键盘读取一行,发送到服务端,并且使用readLine()方法等待服务端回送,输出。
  需要注意的是,整个示例中用到的serverSocket.accept()方法、流的readLine()方法等 都是阻塞的。

  以上便完成了Socket TCP连接的示例.作为一个小白,我把自己如何理解网络编程和如何开启自己Java对Socket进行网络编程的思路都记录在这里。当然对于这个示例还有更多的思考点,为什么要用TCP协议来实现数据交互呢,是否有别的方式? 为什么两台计算机之间进行交互必须要有一台作为服务器,假如我希望A主动连接B是否B机器也需要启动一个SocketServer进行监听呢?

你可能感兴趣的:(网络编程,Java,socket)