Author:MTingle
major:人工智能
Build your hopes like a tower!
文章目录
[二.TCP / UDP 特点](#二.TCP / UDP 特点)
[三.UDP 回显服务器](#三.UDP 回显服务器)
[UDP 服务器](#UDP 服务器)
[UDP 客户端](#UDP 客户端)
[四.TCP 回显服务器](#四.TCP 回显服务器)
[TCP 服务器](#TCP 服务器)
[TCP 客户端](#TCP 客户端)
一.客户端VS服务器
在网络中,主动发起通信的一方称为"客户端",被动接受的这一方,称为"服务器".同一个程序在不同场景中,可以是客户端,也可能是服务器.客户端给服务器发送的数据,称为"请求"(request),服务器给客户端返回的数据,称为"响应"(response);
客户端与服务器之间的交互有多种模式.
1.一问一答 :一个请求对应一个响应.最常见,例如在"web开发"中.
2.一问多答 :一个请求对应多个相应.这个场景最要涉及到"下载".
3.多问一答 :多个请求对应一个响应.这个场景主要涉及到"上传".
4.多问多答 :一个请求可能对应多个响应,一个响应也可能对应多个请求,主要涉及到"远程控制/远程桌面"
二.TCP / UDP 特点
TCP 的特点是:有连接 可靠传输 面向字节流 全双工
UDP 的特点是:无连接 不可靠传输 面向数据报 全双工
有连接 / 无连接: 此处的连接不是物理意义上的连接,而是抽象,虚拟的连接,举个简单的例子,当我们打电话时,一边拨号,一边接通,此时才能通话,如果一方不接通,就无法进行通话,这就叫做有连接,连接首先的特点是双方都能认同 .无连接类似发短信,无论你是否同意,我都能给你将信息发过去.计算机中的"网络连接"即是通信双方,各自保存对方的信息,客户端中,有一些数据结构记录了谁是他的服务器,服务器中也有一些数据结构,记录了谁是他的客户端~~
可靠传输 / 不可靠传输: 网络上"异常情况"很多,无论使用什么样的软硬件的技术手段,都无法保证网络数据100%从A运送到B .此处我们的"可靠传输"指的是尽可能的完成数据传输,虽然无法保证数据到达对方,但至少可以知道这个数据对方是否收到了,此处的可靠传输,主要指的是发送的数据没收到,发送方能否清楚地感知到.
面向字节流 / 面向数据报 : 此处的字节流和文件中的字节流完全一致,网络中传输的数据的基本单位是字节 .面向数据报,每次传输的单位就是一个数据报(特定的结构,数据报由一系列的字节构成).
全双工 / 半双工: 一个信息渠道可以双向通信称为全双工,只能单向通信成为半双工.
三.UDP 回显服务器
核心的类有两个:
1.DatagramSocket: 负责对 socket 文件读写,也就是借助网卡发送数据
2.DatagramPacket: 面向数据报,每次发送接收数据的基本单位,就是一个UDP数据报
UDP 服务器
java
public class UdpEchoServer {
DatagramSocket socket=null;
// 第一步: 创建DatagramSocket对象,而后的操作针对socket对象完成
public UdpEchoServer(int port) throws IOException {
socket=new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) { // 对于服务器,需要不断接受请求,返回响应,于是需要用一个 while 循环
// 1.接受请求并解析,byte可以保存收到的消息正文
DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
String request=new String(requestPacket.getData(),0,requestPacket.getLength());
// 2.根据请求响应计算
String response=process(request);
DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
// 3.返回响应
socket.send(responsePacket);
System.out.printf("[%s:%d] req:%s, resp:%s\n",responsePacket.getAddress(),responsePacket.getPort(),
request,response);
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer udpEcohoServer=new UdpEchoServer(9090);
udpEcohoServer.start();
}
}
UDP 客户端
java
public class UdpEchoClient {
private DatagramSocket socket=null;
private String serverIp;
private int serverPort;
public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
this.serverIp=serverIp;
this.serverPort=serverPort;
socket=new DatagramSocket();
}
public void start() throws IOException {
System.out.println("客户端启动!");
Scanner scanner=new Scanner(System.in);
while (true) {
System.out.printf("-> ");
// 1.从控制台读取流程
if (!scanner.hasNext()) {
break;
}
String request=scanner.next();
// 2.构造请求并发送
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serverIp),serverPort);
socket.send(requestPacket);
// 3.读取服务器响应
DatagramPacket responsePacker=new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacker);
// 4.把响应显示在控制台上
String response=new String(responsePacker.getData(),0,responsePacker.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);
client.start();
}
}
UDP字典
java
public class UdpDictServer extends UdpEchoServer {
private HashMap<String,String> hashMap=new HashMap<>();
public UdpDictServer(int port) throws IOException {
super(port);
hashMap.put("小鸡","chicken");
hashMap.put("小猫","cat");
hashMap.put("小狗","dog");
}
@Override
public String process(String request) {
return hashMap.getOrDefault(request,"该词典中没有这个单词");
}
public static void main(String[] args) throws IOException {
UdpDictServer udpDictServer=new UdpDictServer(9090);
udpDictServer.start();
}
}
四.TCP 回显服务器
TCP 服务器
java
public class TcpEchoServer {
private ServerSocket serverSocket=null;
public TcpEchoServer(int port) throws IOException {
serverSocket=new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务端启动!\n");
while (true) {
// 通过accept"接听电话"后才能进行通信
Socket clientSocket=serverSocket.accept();
Thread t=new Thread(()->{ // 引入多线程,可以同时服务多个服务器
processConnection(clientSocket);
});
t.start();
}
}
private void processConnection(Socket clientSocket) {
System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
// 循环读取客户请求并反映
try (InputStream inputStream= clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream()){
while (true) {
// 通过inputStream读取数据
Scanner scanner=new Scanner(inputStream);
if (!scanner.hasNext()) {
System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
break;
}
// 1.读取请求并解析,这里有隐藏约定,next读的时候要以 空格 或 换行符 结束;
String request=scanner.next();
// 2.响应请求并计算
String response=process(request);
// 3.把响应返回给客户端
//通过这种方式可以写回,但是这种方式不方便给返回的响应中添加\n
// outputStream.write(response.getBytes(),0,response.getBytes().length);
// 也可以给 outputstream 套上一层,完成更方便的写入。
PrintWriter printWriter=new PrintWriter(outputStream);
printWriter.println(response);
printWriter.flush();
System.out.printf("[%s:%d] req:%s, resp:%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
request,response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer tcpEcohoServer=new TcpEchoServer(9090);
tcpEcohoServer.start();
}
}
TCP 客户端
java
public class TcpEchoClient {
private Socket socket=null;
public TcpEchoClient(String serverIP, int serverPort) throws IOException {
socket=new Socket(serverIP,serverPort);
}
public void start() throws IOException {
System.out.println("客户端上线!\n");
// 1.从控制台读取输入的字符串
try (InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream()){
Scanner scannerConsole=new Scanner(System.in);
Scanner scannerNetwork=new Scanner(inputStream);
PrintWriter writer=new PrintWriter(outputStream);
while (true) {
System.out.print("-> ");
if (!scannerConsole.hasNext()) {
break;
}
String request=scannerConsole.next();
// 2.把请求发送给服务器,这里需要有 println 来发送,为了让发送结尾带 \n
// 这里是和 scanner.next() 呼应
writer.println(request);
// 主动刷新缓冲器,确保数据发送
writer.flush();
// 3.从服务器读取响应
String response=scannerNetwork.next();
// 4.把响应显示出来
System.out.println(response);
}
}finally {
socket.close();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client=new TcpEchoClient("127.0.0.1",9090);
client.start();
}
}