重庆h5建站模板,wordpress 纯净主题,自己怎么设计公主裙,现货做网站目录 前言#xff1a;
1.网络编程的基础
1.1为什么需要网络编程
1.2什么是网络编程
1.3网络编程中的基本概念
1.3.1发送端和接收端
1.3.2请求和响应
1.3.3客户端和服务端
2.Socket套接字
2.1概念
2.2分类
3.UDP数据报套接字编程
3.1DataGramSocket API
3.2Datagr…目录 前言
1.网络编程的基础
1.1为什么需要网络编程
1.2什么是网络编程
1.3网络编程中的基本概念
1.3.1发送端和接收端
1.3.2请求和响应
1.3.3客户端和服务端
2.Socket套接字
2.1概念
2.2分类
3.UDP数据报套接字编程
3.1DataGramSocket API
3.2DatagramPacket API
3.3基于UDP的回显服务器echo server
3.4简单的翻译服务器
4.TCP流套接字
4.1ServerSocket API
4.2Socket API
4.3基于TCP的回显程序
5.再谈协议
结束语 前言
在上一节中小编主要是与大家分享了一些有关于网络的基础知识但是里面的细节和基础的编程还没有给大家来交代这节中小编就给大家俩交代一下有关于网络基础编程方面的一些基础的编程吧大家赶快跟上小编的步伐一起来往下看吧。如果还没有看小编网络基础知识的部分的同学建议先去看看这篇博文吧☞http://t.csdn.cn/aj9ov
1.网络编程的基础
1.1为什么需要网络编程
用户在浏览器中打开在线视频网站比如抖音短视频其实质是通过网络获取到网络上的一个视频资源与本地打开视频文件类似只是视频文件这个资源的来源是网络相比本地资源来说网络提供了更为丰富的网络资源所谓的网络资源其实就是在网络中可以获取的各种数据资源而所有的网络资源都是通过网络编程来进行数据传输的。
1.2什么是网络编程
网络编程指网络上的主机通过不同的进程以编程的方式实现网络通信网络数据传输。
当然我们只要满足进程不同就行所以即便是同一个主机只要是不同进程基于网络来传输数据也属于网络编程。
1.3网络编程中的基本概念
1.3.1发送端和接收端
在一次网络数据传输时
发送端数据的发送方进程称为发送端。发送端主机即网络通信中的源主机。接收端数据的接收方进程称为接收端。接收端主机即网络通信中的目的主机。收发端发送端和接收端两端也简称Wie收发端。
注意发送端和接收端只是相对的概念只是一次网络数据传输产生数据流向后的概念。
1.3.2请求和响应
一般来说获取一个网络资源涉及到两次网络数据传输。
第一次请求数据的发送。第二次响应数据的发送。
就像是在餐厅点饭一样先发起请求点一份蛋炒饭。餐厅在给一个响应提供一份蛋炒饭。
1.3.3客户端和服务端
服务端在常见的网络数据传输的场景下把提供服务的这一方进程称为服务端可以提供对外服务。客户端获取服务的一方进程称为客户端。
对于服务来说一般是提供
客户端获取服务资源。客户端保存资源在服务端。
就像是我们在银行办事
银行提供存款服务用户客户端保存现金资源在银行服务端。银行提供取款服务用户客户端获取服务端资源银行替用户保管现金。
2.Socket套接字
2.1概念
Socket套接字是操作系统提供给应用程序的一组用于网络编程的API。他是基于TCP/IP协议的通信的的基本操作单元。
注意操作系统原生的Socket API是C语言的但是这里我们学习的是Java封装之后的版本。
2.2分类
Socket套接字主要针对传输层协议划分为如下三类
数据报套接字使用传输层UDP协议。
UDP即User Datagram Protocol用户数据报协议传输层协议。它的特点是无连接、不可靠传输、面向数据报、全双工。
流套接字使用传输层TCP协议。
TCP即Transmission Control Protocol传输控制协议传输层协议。它的特点是有连接、可靠传输、面向字节流、全双工。
对于字节流来说可以简单理解为传输的数据是基于IO流的流式数据的特征就是在IO流没哟关闭的情况下是无边界的数据可以多次发送也可以分开多次接收。
原始套接字
原始套接字用于自定义传出层协议用于读写内核没有处理的IP协议数据这里我们对此不做过多讨论我们重点是理解和应用前两个。
TCP特点vsUDP特点
UDP特点TCP特点无连接使用UDP通信双方不需要刻意保存对方的相关信息有连接使用TCP通信双方则需要刻意保存对方的相关信息不可靠传输消息发了就发了不关注结果可靠传输不是说发送之后对方就可以100%能够达到对方这要求就太高了只是说尽可能的传输过去。面向数据报以UDP数据报为基本单位。面向字节流以字节为传输的基本单位读写方式非常灵活全双工一条路径双向通信全双工一条路径全向通信。
解释全双工vs半双工。
全双工是一条路径全向通信你可以理解为一个双向通道的马路。半双工是一条路径只能由一侧向另一侧通信你可理解为单向通道的马路。
针对上述的TCP协议和UDP协议也给我们提供了两组不同的API。下面我们来一步一步的了解一下。
3.UDP数据报套接字编程
3.1DataGramSocket API
DataGramSocket 是UDP Socket用于发送和接收UDP数据报所谓Socket是一个特殊的文件是网卡这个硬件设备的抽象表示你也可以理解为是一个遥控器想要进行网络通信就需要有socket文件这样的对象借助这个socket文件对象才能够间接的操作网卡。
往这个socket对象里写数据相当于通过网卡发送消息。从这个socket对象中读数据相当于通过网卡接收消息。
DatagramSocket的构造方法可以绑定一个端口号服务器也可以不显示指定客户端。
方法签名方法说明DatagramSocket()创建一个UDP数据报套接字的Socket绑定到本机任意一个随机端口一般用于客户端DatagramSocket(int port)创建一个UDP数据报套接字的Socket绑定到本机指定的端口一般用于服务端
服务器这边的socket往往要关联一个具体的端口号。客户端这边则不需要手动指定系统会自动分配一个闲置的端口号。
举个例子 比如现在我开了一家餐厅要发传单那么在传单上面我这边可定是要标清楚我的餐厅的具体位置在哪窗口号是多少都得事先分配好此时我开的这家餐馆就相当于是服务器确定的地址和窗口号就是服务器事先分配好的端口号。那么如果此时客人看到我发的传单就来到我的餐馆吃饭了那么它点完餐之后就会随便找一个空着的位置坐下等饭。此时客人就相当于是客户端随便找的位置就是系统给随机分配的一个空闲的端口号。 DatagramSocket方法
方法签名方法说明void receive(DatagramPacket p)从此套接字接收数据报如果没有接收到数据报该方法会阻塞等待void send(DatagramPacket p)从此套接字发送数据报不会阻塞等待直接发送void close()关闭数据报套接字释放资源
注意
DatagramPacket表示一个UDP数据报。在close的时候到底啥时候调用close一定是要socket/文件确定一定以及肯定不再使用此时才能调用close。
3.2DatagramPacket API
DatagramPacket是UDPSocket发送和接收的数据报。
DatagramPacket构造方法
方法签名方法说明DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报接收的数据保存在字节数组第一个参数buf中接收指定长度第二个参数lengthDatagramPacket(byte[] buf, int offset, int lenght, SocketAddress address)构造一个DatagramPacket以用来发送数据报发送的数据为字节数组第一个参数buf中从0到指定长度第二个参数length,address指定目的的主机的IP和端口号。 DatagramPacket方法
方法签名方法说明InetAddress getAddress()从接收的数据报中获取发送端主机IP地址或从发送的数据报中获取接收端主机IP地址。int getPort()从接收的数据报中获取发送端主机的端口号或从发送的数据报中获取接收端主机端口号。byte[] getData()获取数据报中的数据。
3.3基于UDP的回显服务器echo server
介绍了DatagramSocket 和 DatagramPacket API之后我们基于UDP socket写一个简单的客户端服务器程序。 也就是让客户端发一个请求在服务器上返回一个一模一样的响应。
首先来明确一点一个服务器主要做的三个核心的工作
读取请求并解析。根据请求并计算响应。代码中省略了把响应返回给客户端。
服务端代码
package network;
//服务端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer{//需要先定义一个Socket对象//通过网络通信必须要使用socket对象private DatagramSocket socket null;//绑定一个端口不一定能成功//如果某个端口已经被别的进程占用了此时这里的绑定操作就会出错。//同一个主机上一个端口同一个时刻只能被一个进程绑定public UdpEchoServer(int port) throws SocketException {//构造socket的同时指定要关联/绑定的端口。socket new DatagramSocket(port);}//启动服务器的主逻辑public void start() throws IOException {System.out.println(服务器启动成功);while (true) {//每次循环要做三件事//1.读取请求并解析// 构造空饭盒DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096);// 食堂大妈给饭盒里打菜(饭从网卡上来)//这里的receive会阻塞等待等到客户端那边发送数据过来socket.receive(requestPacket);//为了方便处理这个请求需要把数据报转换成StringString request new String(requestPacket.getData(), 0, requestPacket.getLength());//2.根据请求计算响应(此处省略这个步骤)String response process(request);//3.把响应结果写回到客户端// 根据response 字符串构造一个DatagramPacket// 和请求packet 不同此处构造响应的时候需要指定这个包要发给谁,这里调用requestPacket.getSocketAddress()就可以得知了DatagramPacket responsePacket new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//打印一下请求的地址和请求的端口号以及请求的内容和响应的内容System.out.printf([%s:%d] req: %s, resp: %s\n, requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);}}//这个方法是希望我们根据请求计算响应。//由于咱们写的是个回显程序请求是啥响应就是啥//如果后续写一个别的服务器不再回显了而是具有具体的业务了就可以修改process方法//根据需求来重新构造响应//之所以单独列成一个方法就是想让大家知道这个是一个关键的环节。private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer new UdpEchoServer(9090);udpEchoServer.start();}
}客户端代码:
package network;
//客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket null;private String serverIP;//服务器的地址private int serverPort;//服务器的端口//客户端启动需要知道服务器在哪里public UdpEchoClient(String serverIP, int serverPort) throws SocketException {//对于客户端来说不需要显示关联空闲的端口//不代表没有端口而是系统自动分配了一个空闲的端口socket new DatagramSocket();this.serverIP serverIP;this.serverPort serverPort;}public void start() throws IOException {//通过这个客户端可以多次和服务器进行交互Scanner scanner new Scanner(System.in);while (true) {//1.先从控制台读取一个字符串过来//先打印一个提示符提示用户要输入内容System.out.println(-);String request scanner.next();//2.把字符串构造成UDP packet并进行发送DatagramPacket requestPacket new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIP), serverPort);socket.send(requestPacket);//3.客户端尝试读取服务器返回的响应DatagramPacket responsePacket new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);//4.把响应数据转换成String显示出来String response new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.printf(req: %s, resp: %s\n, request, response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient new UdpEchoClient(127.0.0.1, 9090);udpEchoClient.start();}
}启动服务器和客户端结果展示 注意这里一定是先启动服务器再启动客户端
执行流程如下所示 注意在上述过程中我们是客户端和服务器在同一个主机上使用的是127这个IP不同主机则就写实际的IP即可。
在上述通信过程中站在客户端发送数据的角度
源IP是127.0.0.1源端口是64982他是系统自动分配的空闲端口。目的IP是127.0.0.1目的端口是9090
在上述过程中就有同学好奇了不是说是要使用close来关闭资源的吗为什么在代码中好像没有看到释放资源这一步其实对于UdpEchoServer来说这个socket对象是出了循环就不用了但是循环结束意味着start结束意味着main方法结束同时意味着进程结束那么此时进程都结束了所以的资源也就自然释放了所以就不必显示释放资源了。
3.4简单的翻译服务器
在上述中我们编写的是一个回显服务器它是没有实际意义的。那么如何写一个提供实在价值的服务器呢当响应和请求不一样了响应是根据不同的请求计算得到的这里就需要我们对上述过程没有写的process方法来进行编写那么下来我们就具体来实现一下。我们就来编写一个简单的英文单词翻译服务器请求是一个英文单词响应是这个单词的中文翻译。
服务端代码展示
package network;
//词典查询服务端
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;//使用继承是为了复用之前的代码
public class UdpDicServer extends UdpEchoServer{private MapString, String dict new HashMap();public UdpDicServer(int port) throws SocketException {super(port);dict.put(dog, 小狗);dict.put(cat, 小猫);dict.put(tiger, 老虎);//注意这里可以无限添加很多个数据}Overridepublic String process(String request) {return dict.getOrDefault(request,该单词没有查到);}public static void main(String[] args) throws IOException {UdpDicServer udpDicServer new UdpDicServer(9090);udpDicServer.start();}
}客户端代码展示
package network;
//客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket null;private String serverIP;//服务器的地址private int serverPort;//服务器的端口//客户端启动需要知道服务器在哪里public UdpEchoClient(String serverIP, int serverPort) throws SocketException {//对于客户端来说不需要显示关联空闲的端口//不代表没有端口而是系统自动分配了一个空闲的端口socket new DatagramSocket();this.serverIP serverIP;this.serverPort serverPort;}public void start() throws IOException {//通过这个客户端可以多次和服务器进行交互Scanner scanner new Scanner(System.in);while (true) {//1.先从控制台读取一个字符串过来//先打印一个提示符提示用户要输入内容System.out.println(-);String request scanner.next();//2.把字符串构造成UDP packet并进行发送DatagramPacket requestPacket new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIP), serverPort);socket.send(requestPacket);//3.客户端尝试读取服务器返回的响应DatagramPacket responsePacket new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);//4.把响应数据转换成String显示出来String response new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.printf(req: %s, resp: %s\n, request, response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient new UdpEchoClient(127.0.0.1, 9090);udpEchoClient.start();}
}运行结果展示 注意在上述编写服务端代码时我们是直接使用了继承重写了父类的process方法。这样就减少了我们的工作。
4.TCP流套接字
在TCP中有两个核心的类
ServerSocket是给服务器使用的。Socket即会给客户端使用又会给服务器端使用。
下面我们就来分别看看ServerSocket和Socket的具体使用方法。
4.1ServerSocket API
他是创建服务端使用的API。
SocketSocket构造方法
方法签名方法说明ServerSocket(int port)创建一个服务流套接字Socket并绑定到指定端口
SocketSocket方法
方法签名方法说明Socket accept()开始监听指定端口创建时绑定的端口有客户端连接后返回一个服务端Socket对象并基于该Socket建立与客户端的连接否则阻塞等待。void close()关闭此套接字
这里的accept意思就是接收在客户端主动向服务器发起连接请求服务器就得同意一下但是实际上的这个accept又和我们上述给大家解释的意思不太一样这里的accept只是在应用层面的接收实际的TCP连接的接受是在该内核里已经完成了。这个后面在将TCP的时候会给大家交代的。
4.2Socket API
Socket是客户端Socket或服务端中接收到客户端建立连接accept方法的请求后返回的服务端Socket。
不管是客户端还是服务端Socket都是双方建立连接以后保存的对端的信息及用来与对方收发数据的。
Socket的构造方法
方法签名方法说明Socket(String host int port)创建一个客户端流套接字Socket并与对应IP的主机上对应端口的进程建立连接。
这里的host和port指的是服务器的IP和端口TCP是有连接的在客户端new Socket对象的时候就会尝试和指定IP端口的目标建立连接了。
Socket的方法
方法签名方法说明InetAddress getInetAddress()返回套接字所连接的地址InputStream getInputStream()返回此套接字的输入流OutPutStream getOutStream()返回此套接字的输出流 InputStream getInputStream()和OutPutStream getOutStream()是字节流就可以通过上述字节流对象进行数据传输了。
从 InputStream 这里读数据就相当于是从网卡接收。往 OutPutStream 这里写数据就相当于从网卡发送。
注意
这个Socket和DatagramSocket定位类似都是构造的时候指定一个具体的端口让服务器绑定该端口但是ServerSocket一定要绑定具体的端口。
4.3基于TCP的回显程序
服务端代码展示
package network;
//服务端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TcpEchoServer {//serverSocket只有一个clientSocket会给每一个客户都分配一个private ServerSocket severSocket null;public TcpEchoServer(int port) throws IOException {severSocket new ServerSocket(port);}public void start() throws IOException {ExecutorService executorService Executors.newCachedThreadPool();System.out.println(服务器启动成功);while (true) {Socket clientSocket severSocket.accept();//如果直接调用该方法会影响这个循环的二次执行导致accept不及时了。//创建新的线程用新线程来调用processConnection//每次来一个新的客户端都搞一个新的线程即可
// Thread t new Thread(() - {
// try {
// processConnection(clientSocket);
// } catch (IOException e) {
// e.printStackTrace();
// }
// });
// t.start();executorService.submit(new Runnable() {Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}//读取这个方法来处理一个连接//读取请求//根据请求计算响应//把响应返回给客户端private void processConnection(Socket clientSocket) throws IOException {System.out.printf([%s:%d] 客户端上线\n, clientSocket.getInetAddress().toString(), clientSocket.getPort());//使用try()这种写法()中允许写多个流对象使用;来分隔try(InputStream inputStream clientSocket.getInputStream();OutputStream outputStream clientSocket.getOutputStream()) {//没有这个scanner和printWriter完全可以但是代价就是得一个字节一个字节扣找到哪个是请求结束的标记\n//不是不能做而是代替代码比较麻烦//为了简单把字节流包装成了更方便的字符流Scanner scanner new Scanner(inputStream);PrintWriter printWriter new PrintWriter(outputStream);while (true) {//1.读取请求//采用hasNext判定接下来还有没有数据了如果对端关闭了连接(客户端关闭连接)此时hasNext就会返回false循环就结束if (!scanner.hasNext()) {//读取的流到了结尾了(对端关闭了)System.out.printf([%s:%d] 客户端下线了\n, clientSocket.getInetAddress().toString(), clientSocket.getPort());break;}//直接使用scanner读取一段字符串//next会一直往后读读到空白符结束(空格、换行、制表符、翻页符...都算空白符)//nextLine只是读到换行符结束所以这里没有使用它String request scanner.next();//2.根据请求计算响应String response process(request);//3.把响应写回给客户端不要忘记了响应也是要带上换行的//返回响应的时候要把换行符加回来方便客户端那边来区分从哪里到哪里是一个完整的响应。printWriter.println(response);//flush当数据不够大的时候直接进行强制刷新将缓冲区中的数据发给客户端printWriter.flush();System.out.printf([%s:%d] req: %s; resp: %s\n, clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);}}catch (IOException e) {e.printStackTrace();}finally {//clientSocket只是一个连接提供服务的这个还是要进行关闭的clientSocket.close();}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer new TcpEchoServer(9090);tcpEchoServer.start();}
}客户端代码展示
package network;
//客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {private Socket socket null;public TcpEchoClient(String serverIP, int port) throws IOException {//这个操作相当于让客户端和服务器建立TCP连接//这里的连接连上了服务器的accept就会返回socket new Socket(serverIP, port);}public void start() throws IOException {Scanner scanner new Scanner(System.in);try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {PrintWriter printWriter new PrintWriter(outputStream);Scanner scannerFromSocket new Scanner(inputStream);while (true) {//1.从键盘上读取用户输入的内容System.out.println(-);String request scanner.next();//2.把读取到的内容构造成请求发送给服务器//注意这里的发送是带有换行的。printWriter.println(request);printWriter.flush();//3.从服务器读取响应的内容String response scannerFromSocket.next();//4.把响应结果显示到控制台上System.out.printf(req: %s; resp: %s\n, request, response);}}}public static void main(String[] args) throws IOException {TcpEchoClient client new TcpEchoClient(127.0.0.1, 9090);client.start();}
}结果展示 执行流程如下所示 那么这里我们只是启动了一个客户端在实际中不可能是一个服务器只给一个客户端进行服务那么如何启动多个客户端呢这里在idea中是默认下只能启动一个的那么这里我们需要打开idea配置一下。配置过程如下所示 此时当我们再次点击上述的三角形就可以再次启动另一个客户端了。 5.再谈协议
回顾并理解我们为什需要协议
以上我们实现的UDP和TCP数据传输除了UDP和TCP之外程序还存在应用层定义协议可以想想分别都是什么样的协议格式。
对于客户端及服务端应用程序来说请求和响应需要约定一致的数据格式
客户端发送请求和服务端解析请求和要使用相同的数据格式。服务端返回响应和客户端解析响应也要使用相同的数据格式。请求格式和响应格式可以相同也可以不同。约定相同的数据格式主要目的是为了让接收端在解析的时候明确如何解析数据中的各个字段。可以使用知名协议广泛使用的协议格式如果想自己约定数据格式就属于自定义协议。
结束语
这节中小编主要是和大家分享了网络编程中的两个重要的编程UDP和TCP后期小编还会继续出有关于网络方面的知识的希望这节对大家了解网络有一定帮助想要学习的同学记得关注小编和小编一起学习吧如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津在此小编先谢过各位大佬啦