咱们的一个TCP服务器,是否可以让一个UDP客户端连接上呢?
1)TCP和UDP,他们无论是API代码,还是协议底层的工作过程,都是差异巨大的,不是单纯的把流转化成数据包就可以的;
2)描述一次通信,我们需要使用5元组,协议类型不匹配,通信是无法进行完成的,也就是说咱们的通信双方要使用同一层协议才可以进行通信;
回顾:利用UDP来创建一个字典服务器:
import java.io.IOException; import java.net.DatagramPacket; import
java.net.DatagramSocket; import java.net.SocketException; import
java.util.HashMap; public class Server { int port; DatagramSocket socket=null;
HashMap<String,String> map=new HashMap<>(); public Server(int port)throws
SocketException { this.port = port; this.socket =new DatagramSocket(port);
map.put("及时雨","宋江"); map.put("国民女神","高圆圆"); map.put("大聪明","李嘉欣");
map.put("王者大神","李佳伟"); } public void start()throws IOException {
System.out.println("服务器即将启动"); while(true) { DatagramPacket requestpacket=new
DatagramPacket(new byte[4096],4096); socket.receive(requestpacket); String
request=new String(requestpacket.getData(),0,requestpacket.getLength()); String
response=processon(request); DatagramPacket responsepacket=new
DatagramPacket(response.getBytes(),response.getBytes().length,requestpacket.getSocketAddress());
socket.send(responsepacket); String str=String.format("[%s:%d]
request=%s;reponse=%s",requestpacket.getAddress(),requestpacket.getPort(),request,response);
System.out.println(str); } } public String processon(String request) { return
map.getOrDefault(request,"你查询的词不存在"); // return request; } public static void
main(String[] args)throws SocketException,IOException{ Server server=new
Server(9090); server.start(); } } import java.io.IOException; import
java.net.*; import java.util.Scanner; public class user{ DatagramSocket
socket=null; int serverport; String serverIP; public user(int serverport,String
serverIP)throws SocketException { this.serverIP=serverIP;
this.serverport=serverport; this.socket=new DatagramSocket(); } public void
start()throws UnknownHostException, IOException {
System.out.println("客户端即将启动"); while(true){ System.out.println("请输入你的请求");
Scanner scanner = new Scanner(System.in); System.out.println("->"); String
request=scanner.nextLine(); if(request.equals("exit")) {
System.out.println("goodbye"); return; } DatagramPacket requestpacket=new
DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serverIP),serverport); socket.send(requestpacket);
DatagramPacket responsepacket=new DatagramPacket(new byte[4096],4096);
socket.receive(responsepacket); String response=new
String(responsepacket.getData(),0,responsepacket.getLength()); String
str=String.format("request=%s,response=%s",request,response);
System.out.println(str); } } public static void main(String[] args)throws
SocketException,IOException{ user user1=new user(9090,"127.0.0.1");
user1.start(); } }
1)对于咱们的UDP版本的网络编程来说,我们的程序一进行启动,我们就可以直接进行读写了,对于咱们的TCP版本的网络编程来说,我们的程序一进行启动,
客户端需要先和我们的服务器建立连接,服务器必须先进行连接,我们才可以进行读写;
2)咱们的Socket里面的构造方法指定的IP地址+端口号就是指要和我们服务器上面的哪一个端口进行建立连接;
我们如何进行判断是否断开连接?
1.1)咱们的服务器会在进行处理每一个客户端的请求的时候,会进行判定if(scanner.hasNext)判断我们的客户端是否已经读取完成了,
如果我们的客户端断开连接,那么我们的if判定就会返回,服务器读取就会完毕
1.2)如果客户端已经连接了,那么我们的ServerSocket的accept()方法就会返回,如果我们的客户端断开连接了,那么我们的服务器的hasNext()方法就会感知到;
当前我们的TCP服务器同一时刻只能进行处理一个客户端的请求:
1)一个服务器对应多个客户端,此时就需要多次启动这个客户端实例;
现象是当我们启动第一个客户端之后,服务器进行提示上线,当我们启动第二个客户端的时候,服务器此时就没有任何响应了,况且发送请求的时候没有进行任何响应;但是当我们退出客户端的时候,此时神奇的事情出现了,服务器提示了客户端二上线,况且客户端二也得到了服务器的响应,但是此时客户端三没有任何反应,当我们把客户端2的主机退出,那么客户端3给服务器发送的数据就有效了
2)所以当前的服务器,在同一时刻,只可以给一个客户端提供服务,只有前一个客户端下了,下一个客户端才可以上来;
3)当咱们的第一个客户端尝试与服务器建立连接的时候,服务器会与客户端建立连接,这个时候客户端发送数据,服务器就会做出响应,客户端多次发送数据,咱们的服务器就会循环处理请求;
3.1)调用listenSocket的accept方法,与客户端建立连接;
3.2)执行process方法,来循环进行处理客户端给服务器发送过来的请求,除非说第一个客户端断开链接了,否则就无法进行处理其他请求了,
因为它陷在我们的一个procession方法里面无法出来了;
4)但是此时我们的第二个,第三个,第四个客户端想要给服务器发送数据,不可能成功建立连接;
为什么服务器程序只能进行处理一个客户端呢?
咱们可以和客户端进行交互的前提是我们需要先进行调用accept方法,先进行建立连接,也就是说先进性接通电话;
那为什么会出现这种情况呢?
1)原因是在服务器中的hasNext那里在等待第一个客户端发送数据,他并没有退出第一个客户端对应的这个procession这个方法,也就是说直接死在第一个客户端的procession这个方法的while循环里面(一直进行工作),所以整个服务器的程序就卡死在hasNext这个代码块里面了,主函数的外层的while循环无法进行下一轮,也就无法重新进行循环调用第二次accept方法,服务器无法再次调用accept方法与下一个客户端进行三次握手,建立连接;
2)最终造成的结果是,客户端什么时候退出,hasNext()方法就进行返回,procession()方法就进行返回,第一次外层循环结束,进行第二层外层循环,
才有可能继续调用accept()方法
3)所以问题的关键在于,如果第一个客户端没有退出,此时服务器的逻辑就一直在procession里面打转,也就没有机会进行外层循环再次调用accept方法,也就无法再次去处理第二个连接;第一个客户端退出以后,结束里面的循环,结束上一个procession服务器才可以执行到第二个accept,才可以建立连接;
3)我们这个问题就类似于,好像你接了一个电话,和对方你一言,我一语的进行通话,别人再继续给我们进行打电话,我们就没有办法进行接通了
4)咱们解决上述问题,就需要第一次执行的procession方法,不能影响到咱们的下一次循环扫描accept的执行;
1)咱们的accept方法调用一次,就接通一个,如果说我们多多的调用几次,我们就可以多接通几个,所以我们的解决方法就是说:
咱们的调用procession方法和我们的accept方法执行的调用不会相互干扰
2)也就是说不能让咱们的procession方法里面的循环影响到前面accept方法的执行;
3)我们怎么样才可以说让我们的procession方法自己去执行自己的,并且让这个accept执行自己的呢?让我们的accept被反复调用到,又让我们的procession来进行反复地进行处理客户端请求呢?
1)引入多线程之后,保证主线程始终在调用accept,每次都有一个新的连接来创建新线程来处理请求响应,线程都是一个独立的执行流,每一个线程都会执行自己的同一段逻辑并发执行
2)咱们调用accept方法的线程和调用procession方法的线程是互不干扰的呀