写一个应用程序,让这个程序可以使用网络通信,这里就需要调用传输层提供的api,传输层提供协议,主要是两个:
UDP,TCP,它们分别提供了一套不同的api,socket api.
UDP:无连接,不可靠传输,面向数据报,全双工
TCP:有连接,可靠传输,面向字节流,全双工
一个客户端可以连接多个服务器,一个服务器也可以连接多个客户端(多对多)
链接(link)快捷方式
windows上只有软链接(符号链接),通过一个文件,文件的内容保存另一个文件的路径,实现软链接
Linux除了支持软连接,还支持硬链接(两个路径,共享同一个inode节点)
可靠传输,不是说,A给B发信息100%能收到,而是A尽可能的把消息传给B,并且在传输失败或者成功的时候,A能够感知得到
这里的可靠与否,不能说明谁好谁坏,可靠传输想要实现,也是需要一定的成本,所以,有舍才有的,我们应该将这两种属性视为各自的特性
TCP和文件操作类似,都是"流"式的(由于这里传输的单位是字节,称为字节流)
比如:
通过TCP读写100字节的数据
可以一次读100字节
一次读写50字节,分两次
一次读写10字节,分10次
UDP是面向数据报,读写的基本单位,是一个UDP数据报(包含了一系列是数/属性)
全双工:一个通道,可以双向通信
半双工:一个通道,只能单向通信
UDP更简单,先认识UDP的socket api
两个核心的类:
1.DatagramSocket
操作系统,使用文件这样的概念,来管理一些硬件资源,网卡,操作系统也是使用文件的方式来管理网卡的,
表示网卡这样的文件,称为socket文件
Java中的socket对象,对应着系统里的socket文件(最终还是要落到网卡上)
要进行网络通信,必须得先有socket对象
一个客户端的主机,上面运行的程序有很多,可能存在手动指定的端口号被别的程序占用的了,让系统分配,更为合适
2.DatagramPacket
表示UDP数据报,代表了系统中设定的UDP数据报的二进制数据
方法签名 | 方法说明 |
---|---|
DatagramPacket(byte[]buf, int length) | 构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length) |
DatagramPacket(byte[]buf, int offset, int length,SocketAddress address) | 构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号 |
package network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpEchoSerevr {
DatagramSocket socket = null;
public UdpEchoSerevr(int port) throws SocketException {
//这里抛出的异常,最常见的情况是端口号被占用
socket = new DatagramSocket(port);
}
//使用下面的start取调用服务器
public void start() throws IOException {
System.out.println("服务器启动!");
//反复的,长期的执行针对客户端请求处理的逻辑.
while(true){
DatagramPacket packet = new DatagramPacket(new byte[4096],4096);
//一个服务器,在执行过程中,主要执行的是三个核心任务
//1.读取请求,并解析
//下面这个方法中的参数DatagramPacket是一个"输出型参数"
//传入receive的是一个空的对象
//receive内部就会把这个空的对象的内容给填充上,
//当receive执行结束的时候,就会得到一个装满内容的DatagramPacket
socket.receive(packet);
//要将数据报转换为字符串的前提是,客户端发的数据就是一个文本的字符串
String request = new String(packet.getData(),0,packet.getLength());
//2.根据请求,计算响应
String response = process(request);
//3.把响应写回给客户端
//此时要告知网卡,要发送给客户端的内容是什么,要发给谁
//getSocketAddress可以得到客户端主机的ip和端口号
DatagramPacket responseRequest = new DatagramPacket(response.getBytes(),response.getBytes().length,
packet.getSocketAddress());
socket.send(responseRequest);
System.out.printf("[%s:%d] req: %s,resp: %s\n",packet.getAddress().toString(),packet.getPort(),request,response);
}
}
public String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoSerevr serevr = new UdpEchoSerevr(9090);
serevr.start();
}
}
package network;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpEchoClient {
DatagramSocket socket = null;
private String serevrIP;
private int serverPort;
public UdpEchoClient(String IP,int Port) throws SocketException {
serevrIP = IP;
serverPort = Port;
socket = new DatagramSocket();
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("客户端启动!");
while(true){
System.out.println("->");
String request = scanner.next();
//构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。
//构造请求对象,并发给服务器
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serevrIP),serverPort);
socket.send(requestPacket);
//读取服务器的响应,并解析出响应内容
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client = new UdpEchoClient("192.168.38.196" ,9090);
client.start();
}
}