客⼾端和服务端
服务端:在常⻅的⽹络数据传输场景下,把提供服务的⼀⽅进程,称为服务端,可以提供对外服务。
客⼾端:获取服务的⼀⽅进程,称为客⼾端。
Socket套接字
概念
Socket套接字,是由系统提供⽤于⽹络通信的技术,是基于TCP/IP协议的⽹络通信的基本操作单元。基于Socket套接字的⽹络程序开发就是⽹络编程。
socket 是操作系统中特殊的文件,表示"网卡"硬件设备
socket api 传输层,提供给应用层
TCP : 有链接, 可靠传输, 面向字节流, 全双工
UDP : 无链接, 不可靠传输, 面向数据报, 全双工
**链接:**抽象的,虚拟的链接.通信双方,各自保存对方的关键信息(IP和端口)
TCP 有链接,先保存对方的关键信息,然后发送数据
UDP 无链接,不需要保存对方的关键信息,直接发送数据
**不可靠传输:**数据发送出去之后,关系对方是否收到,如果
**可靠传输:**发送出去之后,不管了
**全双工:**同时进行双向通信
半双工: 同一时刻,只能单向通信**(兽医)**
UDP数据报套接字编程
DatagramSocket
Datagram:数据报
创建一个这样的对象,就相当于在系统内核里创建了一个socket文件 . 创建socket文件,也会占用文件描述符表的一个位置,用完之后要记得及时关闭,否则可能会出现**"文件资源泄露"**
DatagramSocket 构造方法
|--------------------------|----------------------------------------------|
| DatagramSocket() | 创建⼀个UDP数据报套接字的Socket,绑定到本机任 意⼀个随机端⼝(⼀般⽤于客⼾端) |
| DatagramSocket(int port) | 创建⼀个UDP数据报套接字的Socket,绑定到本机指 定的端⼝(⼀般⽤于服务端) |
谁主动,谁就是客户端. 谁被动,谁就是服务器.
DatagramSocket 方法
|--------------------------------|--------------------------------------|
| void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该 ⽅法会阻塞等待) |
| void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发 送) |
| void close() | 关闭此数据报套接字 |
DatagramPacket
DatagramPacket 构造方法
|-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| DatagramPacket(byte\[\] buf, int length) | 构造⼀个DatagramPacket以⽤来接收数据报,接收的 数据保存在字节数组(第⼀个参数buf)中,接收指定 ⻓度(第⼆个参数length) |
| DatagramPacket(byte\[\] buf, int offset, int length, SocketAddress address) | 构造⼀个DatagramPacket以⽤来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号 |
DatagramPacket ⽅法:
|--------------------------|--------------------------------------------|
| InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 |
| int getPort() | 从接收的数据报中,获取发送端主机的端⼝号;或从发送的数据报中,获取接收端主机端⼝号 |
| byte\[\] getData() | 获取数据报中的数据 |
InetSocketAddress : 创建⼀个Socket地址,包含IP地址和端⼝号
代码示例
java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpEchoServer {
//先创建 socket对象
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
public void start() throws IOException {
//启动服务器
System.out.println("服务器启动....");
//服务器,需要不断处理客户发送过来的请求
while(true){
//循环一次就是处理一个请求
//1.读取请求并解析
DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
socket.receive(requestPacket);
//为了处理方便
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
//2.根据请求构造响应
String response = process(request);
//3.把响应返回给客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
System.out.printf("[%s:%d]; resp: %s\n",requestPacket.getAddress().toString(),responsePacket.getPort(),request,response);
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
//端口号是一个整数,随意指定
UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
udpEchoServer.start();
}
}
java
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpEchoCient {
private DatagramSocket socket = null;
private String serverIP;
private int serverPort;
public UdpEchoCient(String serverIp,int serverPort) throws SocketException {
socket = new DatagramSocket();
this.serverIP = serverIp;
this.serverPort = serverPort;
}
public void start() throws IOException {
System.out.println("客户端启动");
Scanner sc = new Scanner(System.in);
while (true) {
//1.从控制台读取用户输入
System.out.println("-> ");
String request = sc.next();
//2.构造请求,发送给服务器
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
InetAddress.getByName(serverIP), this.serverPort);
socket.send(requestPacket);
//3.读取服务器响应
DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
socket.receive(responsePacket);
String response = new String(requestPacket.getData(), 0, requestPacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoCient cient = new UdpEchoCient("127.0.0.1",9090);
cient.start();
}
}
**服务器必须要指定固定的端口,**确保客户端随时能找到服务器.
客户端来说,必须不指定固定端口 ,让系统随机分配空闲的端口,避免端口号冲突**(大饼)**