咱们的一个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方法的线程是互不干扰的呀