目录
双项通信:
优化1:
优化2:
一对多:
总结:一对一通信,一对多通信都是TCP通信的实现,都是双项通信,只有在两机之间连接之前,才有服务端和客户端的区分,而在连接之后就不在有服务端和客户端了,双方都可以互相发送消息
首先需要声明:部分图片来自百战尚学堂
客户端(Socket)和服务端(ServerSocket)连接的前提是主机之间已经通过TCP协议进行了三次握手,客户端和服务端建立连接的基础是主机之间已经通过TCP协议通信的基础之上的
而服务端和客户端在连接之后就没有主客之分了
单项通信就是客户端固定位发送端,服务端固定为接收端。而双项通信是没有固定的发送端和接收端,双方都可以互相通信,此时我们通过代码讲解一下:
首先我们需要创建服务端ServerSocket对象,因为如果先创建客户端那客户端跟谁连接呢?然后服务端传递参数监听8888端口,然后通过accept方法获取监听器对象,此时这个方法会导致线程阻塞,直到监听器对象连接到客户端才会返回就绪状态
服务端需等待客户端发送的信息,然后服务端才能向客户端发送信息
然后我们创建客户端Socket对象,通过构造方法并传递服务器的IP地址,以及端口号8888,我们需要先向服务端发送信息,才能接收服务端发送的信息
此时我们就可以通过运行服务端和客户端实现双项通信
但是我们的代码还是不够完善,因为客户端和服务端接收消息都是在同一个线程里的,那么就肯定会涉及到先后的问题而且只能读一句,那么我们在聊天的时候不可能只跟别人说一句话吧,此时我们就需要对代码进行更改,将发送信息和接收消息变为两个线程,让他们并发运行
首先我们现在服务端实现一个发送消息的线程关于代码如何写的,我的注释都有解释,大家请耐心观看一下
然后我们创建接收消息的线程,代码的实现逻辑注释都有解释
然后就是我们的主线程了,此时我们可以看到我们将对应客户端的Socket对象从try的小括号里移了出来,因为这个主线程在运行完之后就会死亡,那么try-with-resource这种语法就会将小括号里可以关的都关闭,如果对应客户端的Socket对象关闭了,那么发送消息的线程和接收消息的线程通过什么获取输入输出流呢?所以对应客户端的Socket对象不能在主线程死亡的时候关闭
而服务端此时的代码就优化完毕了,现在我们来写客户端的发送和接收线程,其实客户端的线程是和服务端一样的所以我们就直接复制了
而客户端的主线程也是一样,客户端Socket对象不能在主线程死亡的时候关闭,所以我们这里就不能使用try-with-resource语法,就用普通的try{...}catch(){...}
验证点对点通信
但是我们可以发现好像这个点对点通信的代码还可以优化,我们看昂,这个服务端和客户端在连接过后就没有主客之分了,而服务端和客户端都有一串相同的代码,那就是发送消息的线程和接收消息的线程,那我们能不能将服务端和客户端写到同一个类中呢?
显然是可以的此时我们创建一个新的类来优化服务端和客户端
首先我们在主类中创建输入,当输入为server,
发送消息的线程还是和刚刚写的一样:
接收消息的线程也一样
此时我们可以通过两次运行此类来创建服务端和客户端,如果不能运行两次同样的类的,找到上边栏的这个按钮
然后点击这个进行配置
然后点击这个
选择允许多实例
然后就可以运行两次相同类了 ,然后我们就可以无限制的发送消息和接收消息了,因为这是两个线程并发运行的,互不干扰
那么学到了这里大家应该已经会了点对点的通信了,那么我们可以实现一下一对多的通信,一对多的通信其实很简单嘛,就是将服务端的监听方法accept()方法循环一下不就可以实现监听器一直循环端口了吗,那此时不就形成了一对多的通信了吗
如果没有理解我们通过代码来实现看看:
我们就还是通过之前的代码更改一下,改的地方不多就在监听端口的时候给了个死循环,让服务端一直监听某个端口,监听到数据过后返回服务端所对应的Socket对象,并且每个对应的服务端Socket对象都创建接收消息和发送消息的线程并启动
而服务端没啥区别,就是把下面创建线程的代码放到了else里边,其实放外边也没所谓,一样的效果
此时我们运行此类,一个服务端,两个客户端