文章目录
- JavaWeb 基础知识 -- 网络编程
- 1.为什么要网络编程?
- 2.什么是网络编程
- 3.网络编程中的基本概念
- (1)发送端和接收端
- (2)请求和响应
- (3)客户端和服务端
- (4)常见的客户端服务端模型
- 4.回显服务器代码
- 服务器代码注意事项
- 5.回显客户端代码
- 客户端代码注意事项
- 运行展示
JavaWeb 基础知识 – 网络编程
本文内容大纲
1.为什么要网络编程?
通过代码来控制,让两个主机的进程之间能够进行数据交互
我们所说的网络编程,就是通过网络来进行通信,那么具体来说就是网络连接的不同主机,在具体来说连接的是两个主机之间的进程
比如:
我使用qq发送了一个消息,这个消息就通过我电脑上的qq客户端进程,先发送给了腾讯的服务器(对应的服务器进程),再由腾讯的服务器进程,把这个消息转发给对方电脑的qq进程
这是我们通过网络编程做的最基础的一件事情,通过网络编程就可以达到一个天涯若比邻的效果,就能够让互联网上的很多主机进行配合工作,那么此时我们写的程序能做的事情就太多太多了…所以网络编程就是一个非常常见的需求场景。
2.什么是网络编程
网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。
当然,我们只要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。特殊的,对于开发来说,在条件有限的情况下,一般也都是在一个主机中运行多个进程来完成网络编程。
但是,我们一定要明确,我们的目的是提供网络上不同主机,基于网络来传输数据资源:
进程A:编程来获取网络资源 进程B:编程来提供网络资源
那么我们再具体进行网络编程时怎么用程序进行网络编程呢?
操作系统就把网络编程的一些相关操作封装起来了,提供了一组API供程序员来使用
这些相关操作都是操作系统提供的功能,访问网络核心的硬件设备:网卡
网卡也是归操作系统来管理的,所以操作系统将网卡做好管理之后,并且给程序员提供一些API,我们直接使用API就可以进行网路通信的操作了,这就是网络编程的基本情况
操作系统提供的网络编程API,我们给起了一个名字,叫 scoket (套接字) API,就是操作系统提供的一组关于网络通信的API接口
socket 的英文原义 :插槽
由于操作系统提供的socket API 是 C语言风格的接口,所以在Java当中是没办法直接使用的, JDK就针对C语言的socket API 进行了封装, 在标准库中就有了一组类,这组类就能够让我们进行网络编程,但是我们要知道,这组类本质上仍然是调用的操作系统提供的scoket API
那么我们这里有一个问题?
Java能够调用C语言的函数吗?
可以的,不同的语言之间,很多都可以进行相互调用。要想跨语言调用,核心原理在于了解对应的语言的ABI(二进制编程接口)二进制指令规则等
3.网络编程中的基本概念
(1)发送端和接收端
在一次网络数据传输时:
发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
收发端:发送端和接收端两端,也简称为收发端。
注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。
(2)请求和响应
一般来说,获取一个网络资源,涉及到两次网络数据传输:
第一次:请求数据的发送 第二次:响应数据的发送。
好比在快餐店点一份炒饭: 先要发起请求:点一份炒饭,再有快餐店提供的对应响应:提供一份炒饭
(3)客户端和服务端
服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
客户端:获取服务的一方进程,称为客户端。
对于服务来说,一般是提供:
客户端获取服务资源
好比在银行办事:
银行提供存款服务:用户(客户端)保存资源(现金)在银行(服务端)
银行提供取款服务:用户(客户端)获取服务端资源(银行替用户保管的现金)
(4)常见的客户端服务端模型
最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:
1.客户端先发送请求到服务端
2.服务端根据请求数据,执行相应的业务处理
3.服务端返回响应:发送业务处理结果
4.客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)
这个客户端服务器模型在网络编程中非常常见,我们必须熟悉过程。
为了理解这里的每个过程,咱们还是把一家餐馆比作是一个服务器
然后呢,我现在来到这家餐馆吃饭,首先我要点菜吧,我说来一个回锅肉盖饭,这就是发送请求,然后餐馆把菜给端上来相当于把响应给返回了,而从点餐到上菜之间的这段时间相当于餐馆要去做这个饭,要注意,一家餐馆好不好不就相取决于做饭的过程吗,那做的好生意才好,这个做菜的过程就是根据请求计算响应,菜做好了服务员把菜端到我面前这就是响应显示。
好了,这就是我们给大家介绍的一些关于网络编程的基础知识。我们具体写一个回显服务器及客户端的一个应用场景
4.回显服务器代码
代码语言:javascript复制import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpEchoSever {
private DatagramSocket socket = null;
// port 表示端口号
// 服务器在启动的时候,需要关联上一个端口号
// 收到数据的时候,就会根据这个端口号决定把数据交给哪个进程
// 虽然此处port写的类型式 int,但是实际上端口号是一个两个字节的无符号整数
// 范围: 0~65535
public UdpEchoSever(int port) throws SocketException {
this.socket = new DatagramSocket(port);
}
// 通过这个方法来启动服务器
public void start() throws IOException {
System.out.println("服务器启动!!");
// 服务器一般都是持续运行的 (7*24)
while (true) {
// 1.读取请求,当前服务器不知道客户端啥时候发来请求。receive 方法也会阻塞
// 如果真的有请求过来,此时receive就会返回
// receive 参数DatagramPacket 是一个输出型参数,socket 中读到的数据会设置到这个参数的对象中
// DatagramPacket 在构造的时候需要指定一个缓冲区,通常使用 byte[]
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
// 这个数据包对象要程序员自定义缓冲区大小
socket.receive(requestPacket);
// 把 reuqestPacket 对象里面的内容给取出来,作为一个字符串
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
// 2.根据请求来计算响应
String response = process(request);
// 3.把响应写回给客户,这时候也需要构造一个 DatagramPacket
// 此处给DatagramPacket 设置的长度,必须是“字节的个数”
// 如果直接去response.length() 此处得到的式,字符串的长度,也就是“字符的个数”
// 当前的 responsePacket 在构造的时候,还需要指定这个包要发给谁
// 其实发送的目标,就是发送请求的一方
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
response.getBytes().length,
requestPacket.getSocketAddress()
);
socket.send(responsePacket);
// 4.日志打印
String log = String.format("[%s:%d] req: %s resp: %s",
requestPacket.getAddress().toString(),
requestPacket.getPort(),
request,response);
}
}
// 这里的process 方法负责的功能,就是根据请求来计算响应
// 当前是一个回显服务器,就是把客户端的请求直接返回回去
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoSever udpEchoSever = new UdpEchoSever(9090);
udpEchoSever.start();
}
}
服务器代码注意事项
5.回显客户端代码
代码语言:javascript复制import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpEchoClient {
private DatagramSocket socket = null;
private String severIp;
private int port;
public UdpEchoClient(String severIp,int port) throws SocketException {
this.socket = new DatagramSocket(); // 这里不需要指定端口号,由系统自己分配
this.severIp = severIp;
this.port = port;
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("客户端启动!");
while(true){
// 1.从标准输入读入一个数据,构造请求
System.out.print("->");
String request = scanner.nextLine();
if(request.equals("exit")){
System.out.println("客户端下线!");
return;
}
// 2. 将这个字符串构造成一个Udp请求,并发送数据
// 这里的 DatagramPacket 中,既要包含具体的数据,又要包含这个数据要发给谁
DatagramPacket requestPacket = new DatagramPacket(
request.getBytes(),
request.getBytes().length,
InetAddress.getByName(severIp), port
);
socket.send(requestPacket);
// 3.尝试从服务器读取相应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
// 4.打印日志
String log = String.format("req: %s resp: %s",request,response);
System.out.println(log);
}
}
public static void main(String[] args) throws IOException {
// 环回IP : 127.0.0.1 环回Ip 就相当于主机本身
// 当前,客户端和服务器在同一台主机上,所以客户端中写的服务器 Ip是 127.0.0.1 即可
// 如果在不同主机上,此时就需要写成对应主机的服务器Ip上
UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
udpEchoClient.start();
}
}
客户端代码注意事项
由于是客户端先给服务器发请求(客户端服务器概念的定义决定的)
既然是客户端先发,客户端这边就得先知道服务器的 IP 和 端口号
服务器如果收到了客户端的数据,服务器也就知道了客户端的IP 和端口号
运行展示
在客户端依次输入结果,打印请求和响应,同时服务器打印客户端主机和端口号及请求响应,客户端输入 exit,客户端下线…
今天网络编程就讲到这里,希望大家多多练习,谢谢大家的阅读与欣赏…