Java利用TCP协议实现客户端与服务器通信【附通信源码】

简介:

 

进行TCP协议网络程序的编写,关键在于ServerSocket套接字的熟练使用,TCP通信中所有的信息传输都是依托ServerSocket类的输入输出流进行的。

目录

TCP协议概念

ServerSocket类

服务器端程序

客户端程序


Hello!大家好哇!我是灰小猿!

上一篇博客和大家分享了在网络编程中要注意的基础知识,关于IP、TCP、UDP以及端口和套接字的一些概念,想了解的小伙伴可以看我的这篇文章“盘点那些进行网络编程必须要知道的基础知识”,那么今天大灰狼就来和大家分享一下如何使用TCP/IP进行网络程序的开发。

TCP协议概念

先来了解一下TCP协议的基本概念。

我们知道TCP是可靠而非安全的网络协议。它可以保证数据在从一端送至另一端的时候可以准确的送达,并且抵达的数据的排列顺序和送出时的顺序是相同的。因此在进行TCP协议通信的时候,我们首先应该保证客户端和服务器之间的连接通畅。

而TCP协议程序的编写,仍然是依靠套接字Socket类来实现的,并且利用TCP协议进行通信的两个程序之间是有主次之分的,即一个是服务器的程序,另一个是客户端的程序。因此两者的功能和编写上也略有不同。如下图是服务器与客户端之间进行通信的示意图:

image.gif编辑

以上就是在TCP协议中客户端与服务器建立连接的过程示意图。而在这其中起到关键作用的就是服务器端套接字ServerSocket和客户端套接字Socket。通过这两个套接字来建立服务器和客户端,从而利用其中的函数进行数据的通信。

在ServerSocket类中有很多需要注意的地方,接下来大灰狼和大家分享一下ServerSocket类的具体用法:

ServerSocket类

ServerSocket类存在于Java.net包中,表示服务器端的套接字,在使用时需要首先导入这个类,我们也知道ServerSocket类的主要功能就是通过指定的端口等待来自于网络中客户端的请求并且进行连接。

值得注意的是:服务器套接字一次只能与一个客户端套接字进行连接,因此如果存在多台客户端同时发送连接请求,则服务器套接字就会将请求的客户端存放到队列中去,然后从中取出一个套接字与服务器建立的套接字进行连接,但是服务器端能够容纳的客户端套接字也不是无限的,当请求连接的数量大于最大容纳量时,那么多出来的请求就会被拒接,一般来说队列的默认大小是50。

ServerSocket类的构造方法通常会抛出IOException异常,具体有以下几种形式:

    • ServerSocket():创建非绑定服务器套接字
    • ServerSocket(inr port):创建绑定到特定端口的服务器套接字
    • ServerSocket(int port, int backlog):利用指定的backlog创建服务器套接字,并将其绑定到指定的服务器端口上,
    • ServerSocket(int port, int backlog, InetAddress bindAddress):使用指定的端口,侦听backlog和要绑定到本地的IP地址创建服务器。这种情况适用于计算机上有多个网卡和多个IP地址的情况,用户可以明确的规定ServerSocket在哪块网卡或哪个IP地址上等待用户的连接请求。

    以下是ServerSocket类中一些常用的方法:

    ServerSocket类中常用的方法

    方法

    返回值 说明
    accept() Socket 等待客户机连接,若连接则创建一个客户端套接字
    isBound() boolean 判断ServerSocket的绑定状态
    getInetAddress() InetAddress 返回此服务器套接字的本地地址
    isClosed() boolean 返回服务器套接字的关闭状态
    close() void 关闭服务器套接字
    bind(SocketAddress endpoint) void 将ServerSocket绑定到特定地址(IP地址和端口号)
    getInetAddress() int 返回服务器套接字等待的端口号

    了解了ServerSocket类的基本方法之后,就是如何进行客户端和服务器进行连接的问题了。

    在服务器端我们可以调用ServerSocket类的accpet()方法与请求连接的客户机建立连接,这时会返回一个和客户端相连接的Socket对象,这个时候其实已经连接成功了,使用getInetAddress()方法就可以获取到进行请求的客户机的IP地址。

    对于如何进行客户端和服务器端数据的通信,就要用到数据的输入流和输出流了,服务器端的Socket对象使用getOutputStream()方法获取到的输出流,将指向客户端的Socket对象使用getInputStream()方法获取到的输入流。由此就实现在服务器向客户端发送数据的一个过程,同样的道理,客户端端的Socket对象使用getOutputStream()方法获取到的输出流,将指向服务器端的Socket对象使用getInputStream()方法获取到的输入流。从而实现由客户端向服务器发送数据的过程。

    注意:accpet()方法会阻塞线程的继续执行,如果在对应的接口没有收到客户端的呼叫,则程序会停留在此处,直到获取到客户端的呼叫才会继续向下执行,但是如果服务器没有收到来自客户端的呼叫请求,并且accpet()方法没有发生阻塞,那么通常情况下就是程序出了问题,一般来说可能是使用了一个已经被其他程序占用了的端口号,导致ServerSocket没有绑定成功!遇到这种情况可以尝试更换新的端口号。

    了解了TCP协议的通信过程,接下来就是进行TCP通信程序的书写啦!

    在网络通信中,如果只要求客户机向服务器发送信息,不要求服务器向客户端反馈信息的行为称为“单向通信”,要求客户机和服务器双方互相通信的过程称为“双向通信”,双向通信只不过是比单向通信多了一个服务器向客户端发送消息的过程,

    接下来分别是服务器端和客户端程序的编写:

    服务器端程序

    package server_1;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    public class MyTcp {
      private ServerSocket server;  //设置服务器套接字
      private Socket client;    //设置客户端套接字
      //连接客户端函数
      void getServer()
      {
        try {
          server = new ServerSocket(1100);  //建立服务器 端口为1100
          System.out.println("服务器建立成功!正在等待连接......");
          client = server.accept(); //调用服务器函数对客户端进行连接     
          System.out.println("客户端连接成功!ip为:" + client.getInetAddress()); //返回客户端IP   
          getClientMessage();   //调用信息传输和接收函数
        } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      void getClientMessage()
      {
        try {
          while (true) {
            InputStream is = client.getInputStream(); //获取到客户端的输入流
            byte[] b = new byte[1024];  //定义字节数组
            int len = is.read(b); //由于信息的传输是以二进制的形式,所以要以二进制的形式进行数据的读取
            String data = new String(b, 0,len);
            System.out.println("客户端发来消息:" + data);
            //定义发送给客户端的输出流
            OutputStream put = client.getOutputStream();
            String putText = "我已经收到!欢迎你!";
            put.write(putText.getBytes());  //将输出流信息以二进制的形式进行写入
          }
        } catch (Exception e) {
          // TODO: handle exception
        }
        try {
          //判断客户端字节流不是空,则关闭客户端
          if (server != null) {
            server.close();
          }
        } catch (Exception e) {
          // TODO: handle exception
        }
      }
      public static void main(String[] args) {
        // TODO Auto-generated method stub
        MyTcp myTcp = new MyTcp();  //调用该类生成对象
        myTcp.getServer();  //调用方法
      }
    }

    image.gif

    客户端程序

    package client_1;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    public class MyClient {
      private Socket client;  //定义客户端套接字
      //建立客户端函数
      void getClient()
      {
        try {
          client = new Socket("127.0.0.1", 1100); //建立客户端,使用的IP为127.0.0.1,端口和服务器一样为1100
          System.out.println("客户端建立成功!");
          setClientMessage();   //调用客户端信息写入函数
        } catch (UnknownHostException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      //定义客户端信息写入函数
      void setClientMessage()
      {
        try {   
          OutputStream pt = client.getOutputStream();   //建立客户端信息输出流
          String printText = "服务器你好!我是客户端!";  
          pt.write(printText.getBytes());   //以二进制的形式将信息进行输出
          InputStream input = client.getInputStream();  //建立客户端信息输入流
          byte [] b = new byte[1024];   //定义字节数组
          int len = input.read(b);  //读取接收的二进制信息流
          String data = new String(b, 0,len);
          System.out.println("收到服务器消息:" + data);
        } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        try {
          //如果客户端信息流不为空,则说明客户端已经建立连接,关闭客户端
          if (client != null) {
            client.close();
          }
        } catch (Exception e) {
          // TODO: handle exception
        }
      }
      public static void main(String[] args) {
        // TODO Auto-generated method stub
        //生成客户端类对象
        MyClient myClient  = new MyClient();
        myClient.getClient();
      }
    }

    image.gif

    同时要注意:在客户端和服务器搭建成功之后,应该先打开服务器等待连接,再打开客户端进行连接,同样在进行关闭时,应该先关闭客户端,再关闭服务器。

    以上面程序为例:

    打开服务器等待客户端连接

    image.gif编辑

    打开客户端与服务器连接成功,并且实现双向通信:

    image.gif编辑

    注意:当一台机器上安装了多个网络应用程序时,很可能指定的端口已经被占用,甚至还可能遇到之前运行很好的程序突然卡住的情况,这种情况很可能是端口被别的程序占用了,这时可以运行netstat-help来活的帮助,可以使用命令netstat-an来查看该程序所使用的端口。

    觉得有用记得点赞关注哟!

    灰小猿期待与你一同进步^ω^

    image.gif编辑

    目录
    相关文章
    |
    3月前
    |
    XML Java 编译器
    Java注解的底层源码剖析与技术认识
    Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
    102 7
    |
    4月前
    |
    数据采集 人工智能 Java
    Java产科专科电子病历系统源码
    产科专科电子病历系统,全结构化设计,实现产科专科电子病历与院内HIS、LIS、PACS信息系统、区域妇幼信息平台的三级互联互通,系统由门诊系统、住院系统、数据统计模块三部分组成,它管理了孕妇从怀孕开始到生产结束42天一系列医院保健服务信息。
    73 4
    |
    4月前
    |
    监控 Java 应用服务中间件
    高级java面试---spring.factories文件的解析源码API机制
    【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
    152 2
    |
    16天前
    |
    前端开发 JavaScript Java
    [Java计算机毕设]基于ssm的OA办公管理系统的设计与实现,附源码+数据库+论文+开题,包安装调试
    OA办公管理系统是一款基于Java和SSM框架开发的B/S架构应用,适用于Windows系统。项目包含管理员、项目管理人员和普通用户三种角色,分别负责系统管理、请假审批、图书借阅等日常办公事务。系统使用Vue、HTML、JavaScript、CSS和LayUI构建前端,后端采用SSM框架,数据库为MySQL,共24张表。提供完整演示视频和详细文档截图,支持远程安装调试,确保顺利运行。
    57 17
    |
    23天前
    |
    Java 调度
    【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
    当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
    93 0
    【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
    |
    1月前
    |
    JavaScript 安全 Java
    智慧产科一体化管理平台源码,基于Java,Vue,ElementUI技术开发,二开快捷
    智慧产科一体化管理平台覆盖从备孕到产后42天的全流程管理,构建科室协同、医患沟通及智能设备互联平台。通过移动端扫码建卡、自助报道、智能采集数据等手段优化就诊流程,提升孕妇就诊体验,并实现高危孕产妇五色管理和孕妇学校三位一体化管理,全面提升妇幼健康宣教质量。
    53 12
    |
    10天前
    |
    JavaScript Java Docker
    干货含源码!如何用Java后端操作Docker(命令行篇)
    只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
    |
    1月前
    |
    人工智能 监控 安全
    Java智慧工地(源码):数字化管理提升施工安全与质量
    随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
    44 5
    |
    18天前
    |
    存储 监控 数据可视化
    SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
    智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
    |
    2月前
    |
    JavaScript Java 测试技术
    基于Java+SpringBoot+Vue实现的车辆充电桩系统设计与实现(系统源码+文档+部署讲解等)
    面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!