您的位置:首页 > 教育 > 培训 > 塘厦外发加工网_企业官网网页设计报价_推广app的营销方案_fifa世界排名最新

塘厦外发加工网_企业官网网页设计报价_推广app的营销方案_fifa世界排名最新

2025/2/25 19:34:53 来源:https://blog.csdn.net/2301_80055001/article/details/144383153  浏览:    关键词:塘厦外发加工网_企业官网网页设计报价_推广app的营销方案_fifa世界排名最新
塘厦外发加工网_企业官网网页设计报价_推广app的营销方案_fifa世界排名最新

1. 回显服务器——UDP

一个 UDP 的客户端/服务器通信的程序——回显服务器(echo server):

这个程序只是单纯地调用 Socket API

1)让客户端给服务器发送一个请求,请求就是从控制台输入的字符串

2)服务器收到字符串后,就会把这个字符串返回个客户端,客户端再显示出来

1. 服务器:

服务器和客户端都需要创建Socket对象

服务器的Socket一般要显示指定一个端口

客户端的Socket一般不能显示指定(系统自动分配一个随机的端口)

客户端的端口号是不确定的,交给系统进行分配即可

如果手动指定端口,可能会和其他程序的端口号起冲突

此时Socket对象就绑定到这个指定的端口

先构造一个空的对象,传递到方法内部,由receive内部对这个数据进行填充

DatagramPacket对象来承载从网卡读来的数据

接收数据的时候,需要一个内存来保存这个数据(DatagramPacket内部不能自行分配内存空间)

因此需要把空间创造好,交给DatagramPacket处理

此时服务器一旦启动,就会立即执行到这里的receive方法,客户端的请求可能还没来

receive会直接阻塞,直到客户端发来请求为止

String为取这个区间内的字节,构造成一个String

String request = new String(ruquestPacket.getData(),0,ruquestPacket.getLength());

此处的 ruquestPacket.getLength() 是收到数据的真实长度(取决于发送方发送了多少数据)

 此处是服务器程序的核心步骤,但现在实现的是回显,相当于是把请求当成相应

UDP 是无连接的,每次发送的时候,重新指定数据要发送到哪里去

new DatagramPocket() 
构造数据报,指定数据内容,也指定数据报要发给谁
response.getBytes(),response.getBytes().length   数据的内容
ruquestPacket.getSocketAddress()       请求的地址(IP地址和端口号)

response.getBytes().length     

此处使用.getBytes(),是因为进行网络传输的时候,是使用字节的

如果是英文字符,字节和字符的个数是一样的,包含中文就不一样了(字符集)

此处是打印出一个日志

2. 客户端:

此处使用

String request = scanner.next();

是用为了判断不同的数据报,以换行符 /n 为标准

总结:

1)上述代码中为什么没有close

socket文件是文件描述符中的一个表项,每打开一个文件,就会占用一个位置

文件描述符,是在PCB上进行的

private DatagramSocket socket = null;

这个socket是在整个程序运行过程中都是需要使用的(不能提前关闭)

当socket不需要使用的时候,程序也就要结束了

进程结束的时候,文件描述符就会随之摧毁(PCB摧毁)

随着销毁的过程,socket被系统自动回收

文件泄露的时机:

代码中频繁打开文件,但是不关闭

在一个进程的运行过程中,不断打开积累的文件,逐渐消耗文件描述符里的内容,最终消耗殆尽

如果进程的生命周期很短,打开一下没多久就关闭了,也不算泄露

文件泄露一般在服务器一边,在客户端影响不大

2. 3个 DatagramPacket 的构造方法:让数据报带上内容+数据的目的地址

a. 只指定字节数组缓冲区(服务器接受请求的时候使用,客户端接收响应的时候使用)

服务器返回响应给客户端

b. 指定字节数组缓冲区,同时指定一个 InetAddress 对象(这个对象同时包含IP和端口号)

c. 指定字节数组缓冲区,同时指定IP+端口号

需要把IP地址转换一下

1)服务器先启动,服务器启动后,就会进入循环,执行到receive这里并阻塞(此时客户端还没启动)

2)客户端启动,先会进入while循环,执行scanner.next(),并且在这里阻塞

当用户在控制台输入字符串后,next就会返回,从而构造请求数据并发送出来

3)客户端发出数据后:

服务器:从receive中返回,进一步的执行解析请求为字符串,执行process操作,执行sand操作

客户端:继续往下执行,执行到receive,等待服务器的响应

4)客户端收到从服务器返回的数据之后,就会从receive中返回

执行这里的打印操作,也就把响应显示出来了

5)服务器完成一次循环之后,又执行到receive这里

客户端完成一次循环之后,又执行到scanner.next()这里

双双进入阻塞

2. 词典——UDP

start方法中,调用process方法(this.process)

当前是子类引用调用start,this就是指向子类的引用。虽然this是父类的类型,但是实际指向的是子类引用,调用process,自然会执行到子类的方法

虽然没有修改start方法的内容,但是仍然可以确保按照新版本的process来执行 

3. 回显服务器——CDP

两个关键的类:

1. ServerSocket  给服务器使用,使用这个类来绑定端口号

2. Socket  既会给服务器使用,也会给客户端使用

这两类都是用来表示socket文件的(抽象了网卡这样的硬件设备)  

1)TCP 是字节流的,传输的基本单位是 byte

2)UDP 每次发送数据都得手动在send方法中指定目标的地址(UDP 自身没有存储这个信息)

      TCP 不需要上述过程,前提是需要把连接给建立上

3)连接的建立不需要代码干预,是系统内核自动负责完成的

对应用程序来说:
客户端,主要是发起“建立连接”动作

服务器,主要是把建立好的链接从内核中拿到应用程序里

内核中有一个队列(可以视为一个阻塞队列)

如果有客户端,和服务器建立连接,这时服务器的应用程序是不需要做出任何操作的(无感知),内核直接完成建立连接的细节流程(三次握手)。完成流程后,就会在内核的队列中(这个队列是每一个serverSocket都有的一个队列)排队

应用程序想要和这个客户端进行通信,就需要通过一个accept方法把内核中队列中已经建立好的链接对象,拿到应用程序中

实现流程:

getPort()                                     得到对端的端口(客户端)

getInetAddress()                         得到对端的IP(客户端)

getaLocalAddress()                    得到本地的IP(服务器)

getLocalPort()                             到本地的端口(服务器)

clientSocket.getInputStream()  
clientSocket.getOutputStream()

InputStream 和 OutputStream 是字节流,可以通过这两个对象,完成数据的“发送”和“接收”

通过 InputStream 进行read操作——接收

通过 OutputStream 进行write操作——发送                          以字节为单位进行传输

空白字符是一类特殊的字符——换行,回车符,制表符,翻页符,垂直制表符...

后续客户端发起请求,会以空白符作为结束标志(约定使用\n)

TCP 是字节流通信方式,每次传输/读取多少字节都很灵活

需要手动制定出,从哪到哪是一个完整的数据报

每循环一次处理一个数据报

在该过程中存在内存泄露—— clientSocket 这个对象没有进行 close

DatagramSocket 和 ServerSocket 都是在程序中的,只有这么一个对象,生命周期贯穿整个程序

clientSocket 则是再循环中,每次有一个新的客户端建立连接,都会创建出新的clientSocket

并且这个socket最多使用到该客户端退出(断开连接)

try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream())

只是关闭了 clientSocket 上自带的流对象,并没有关闭socket本身

——>需要在try方法末尾,加上close,保证当前这里的socket能够被正确关闭掉

 客户端的实现:

默认情况下,IDEA只允许一个代码创建一个进程

通过上述方法就可以在一个代码中同时创建多个进程

当启动两个客户端同时连接服务器:

其中一个客户端(先启动的客户端),一切正常

另一个客户端(后启动的客户端),无法与服务器建立按连接(服务器不会提示“建立连接”,也不会针对请求做出回应)

第一个客户端过来后,accept就返回了,得到一个clientSocket,进入processConnection

又进入一个while循环,在这个循环中,需要反复处理客户端发来请求数据。如果客户端这会没发请求,服务器的代码就阻塞在scanner.hasNext()这里

此时此刻,第二个客户端也来建立连接了,此时连接是成功建立的(内核负责)

建立成功后,连接对象就会在内核的队列里等待代码通过accept把连接取出来,在代码中处理

——>

第一个客户端会使服务器处于processConnection内部

进一步的也就使当前的第一层循环,无法第二次执行到accept

非得等到第一个客户端退出,processConnection才能结束,从而执行第二次accept

——>

创建新的新的线程,让新的线程调用processConnection

主线程就可以继续执行下一次accept了,新线程内部负责processConnection内部的循环

此时意味着,每次有一个客户端,就得分配一个新的线程

 

刚才出现这个问题的关键在于两重循环在一个线程里

进入第二重循环的时候,无法继续执行第一个循环

UDP 版本的服务器,当时只有一个循环,不存在类似的问题

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com