文章目录
地球村
随着广播、电视、互联网的出现,随着各种现代交通方式的飞速发展,人与人之间的时空距离骤然缩短,整个世界紧缩成一个“村落”。
地球村也译为世界村(global village),对地球的一种比喻说法。现代科技的迅速发展,缩小了地球上的时空距离,国际交往日益频繁便利,因而整个地球就如同是茫茫宇宙中的一个小村落。
计算机网络
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
计算机网络主要是由一些通用的、可编程的硬件互连而成的,而这些硬件并非专门用来实现某一特定目的(例如,传送数据或视频信号)。这些可编程的硬件能够用来传送多种不同类型的数据,并能支持广泛的和日益增长的应用。
数据通信
是计算机网络的最主要的功能之一。数据通信是依照一定的通信协议,利用数据传输技术在两个终端之间传递数据信息的一种通信方式和通信业务。它可实现计算机和计算机、计算机和终端以及终端与终端之间的数据信息传递,是继电报、电话业务之后的第三种最大的通信业务。数据通信中传递的信息均以二进制数据形式来表现,数据通信的另一个特点是总是与远程信息处理相联系,是包括科学计算、过程控制、信息检索等内容的广义的信息处理。
网络编程从大的方面说就是对信息的发送到接收,中间传输为物理线路的作用。
网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。中间最主要的就是数据包的组装,数据包的过滤,数据包的捕获,数据包的分析,当然最后再做一些处理,代码、开发工具、数据库、服务器架设和网页设计这5部分你都要接触。
https://blog.csdn.net/troubleshooter/article/details/122376824网络是一个复杂的系统,不仅包括大量的应用程序、端系统、通信链路、分组交换机等,还有各种各样的协议组成。
为了给网络协议的设计提供一个结构,网络设计者以分层(layer)的方式组织协议,每个协议属于层次模型之一。每一层都是向它的上一层提供服务(service),即所谓的服务模型(service model)。每个分层中所有的协议称为协议栈(protocol stack)。
OSI(Open System Interconnect),即开放式系统互联。 一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互连模型。
OSI 定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),即ISO开放互连系统参考模型。
每一层实现各自的功能和协议,并完成与相邻层的接口通信。OSI的服务定义详细说明了各层所提供的服务。某一层的服务就是该层及其下各层的一种能力,它通过接口提供给更高一层。各层所提供的服务与这些服务是怎么实现的无关。
| OSI 参考模型 | 功能定义 | 设备 | 协议 | 数据单元 |
|---|---|---|---|---|
| 应用层 | 为应用程序提供服务 | 应用程序 | HTTP,HTTPS,FTP,POP3、SMTP、Telnet、SSH、DHCP、DNS | 数据(Data) |
| 表示层 | 数据格式转换和加密 | — | — | — |
| 会话层 | 建立、管理和维护会话 | — | — | — |
| 传输层 | 建立、管理和维护端到端的连接 | 进程、端口 | TCP、UDP | 数据段(Segment) |
| 网络层 | IP地址及路由选择 | 路由器、交换机、防火墙 | IP、ARP、ICMP、IGMP | 数据包(Packet) |
| 数据链路层 | 提供介质访问和链路管理 | 网桥、以太网交换机、网卡 | ARP、RARP、IEEE802.3、PPP、CSMA/CD | 数据帧(Frame) |
| 物理层 | 传输介质 | 中继器、集线器、双绞线 | FE自协商 Manchester MLT-3 4A PAM5 | 数据位(bit) |
应用层
OSI参考模型中最靠近用户的一层,是为计算机用户提供应用接口,也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP等。
实际公司A的老板就是我们所述的用户,而他要发送的商业报价单,就是应用层提供的一种网络服务,当然,老板也可以选择其他服务,比如说,发一份商业合同,发一份询价单,等等。
表示层
表示层提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。
由于公司A和公司B是不同国家的公司,他们之间的商定统一用英语作为交流的语言,所以此时表示层(公司的文秘),就是将应用层的传递信息转翻译成英语。同时为了防止别的公司看到,公司A的人也会对这份报价单做一些加密的处理。这就是表示的作用,将应用层的数据转换翻译等。
会话层
会话层就是负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。
会话层的同事拿到表示层的同事转换后资料,(会话层的同事类似公司的外联部),会话层的同事那里可能会掌握本公司与其他好多公司的联系方式,这里公司就是实际传递过程中的实体。他们要管理本公司与外界好多公司的联系会话。当接收到表示层的数据后,会话层将会建立并记录本次会话,他首先要找到公司B的地址信息,然后将整份资料放进信封,并写上地址和联系方式。准备将资料寄出。等到确定公司B接收到此份报价单后,此次会话就算结束了,外联部的同事就会终止此次会话。
传输层
传输层建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。该层向高层屏蔽了下层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户控制和设定的、可靠的数据通路。我们通常说的,TCP UDP就是在这一层。端口号既是这里的“端”。
传输层就相当于公司中的负责快递邮件收发的人,公司自己的投递员,他们负责将上一层的要寄出的资料投递到快递公司或邮局。
网络层
本层通过IP寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。就是通常说的IP层。这一层就是我们经常说的IP协议层。IP协议是Internet的基础。
网络层就相当于快递公司庞大的快递网络,全国不同的集散中心,比如说,从深圳发往北京的顺丰快递(陆运为例啊,空运好像直接就飞到北京了),首先要到顺丰的深圳集散中心,从深圳集散中心再送到武汉集散中心,从武汉集散中心再寄到北京顺义集散中心。这个每个集散中心,就相当于网络中的一个IP节点。
数据链路层
将比特组合成字节,再将字节组合成帧,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测。
数据链路层又分为2个子层:逻辑链路控制子层(LLC)和媒体访问控制子层(MAC)。
MAC子层处理CSMA/CD算法、数据出错校验、成帧等;LLC子层定义了一些字段使上次协议能共享数据链路层。 在实际使用中,LLC子层并非必需的。
物理层
实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。
快递寄送过程中的交通工具,就相当于我们的物理层,例如汽车,火车,飞机,船。
对等通信:为了使数据分组从源传送到目的地,源端OSI模型的每一层都必须与目的端的对等层进行通信,这种通信方式称为对等层通信。在每一层通信过程中,使用本层自己协议进行通信。
TCP/IP五层协议和OSI的七层协议对应关系如下:
| OSI 参考模型 | TCP/IP 五层模型 |
| 应用层 | 应用层 |
| 表示层 | |
| 会话层 | |
| 传输层 | 传输层 |
| 网络层 | 网络层 |
| 数据链路层 | 数据链路层 |
| 物理层 | 物理层 |
直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯。
IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。
IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
IP协议中还有一个非常重要的内容,那就是给因特网上的每台计算机和其它设备都规定了一个唯一的地址,叫做“IP地址”。由于有这种唯一的地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。
公有地址
公有地址(Public address)由Inter NIC(Internet Network Information Center因特网信息中心)负责。这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问因特网。
私有地址
私有地址(Private address)属于非注册地址,专门为组织机构内部使用。
以下列出留用的内部私有地址:
| 类别 | 最大网络数 | IP地址范围 | 单个网段最大主机数 | 私有IP地址范围 |
|---|---|---|---|---|
| A | 126(2^7-2) | 1.0.0.1-127.255.255.254 | 16777214 | 10.0.0.0-10.255.255.255 |
| B | 16384(2^14) | 128.0.0.1-191.255.255.254 | 65534 | 172.16.0.0-172.31.255.255 |
| C | 2097152(2^21) | 192.0.0.1-223.255.255.254 | 254 | 192.168.0.0-192.168.255.255 |
IPv4
网际协议版本4(英语:Internet Protocol version 4,IPv4),又称互联网通信协议第四版,是网际协议开发过程中的第四个修订版本,也是此协议第一个被广泛部署的版本。IPv4是互联网的核心,也是使用最广泛的网际协议版本,其后继版本为IPv6,直到2011年,IANA IPv4位址完全用尽时,IPv6仍处在部署的初期。
环回地址:
127.0.0.1,指本地机,一般用来测试使用。对于大多数习惯用localhost的来说,实质上就是指向127.0.0.1这个本地IP地址。在操作系统中有个配置文件(windows中路径为C:\WINDOWS\system32\drivers\etc\hosts,Unix/Linux路径为/etc/hosts)将localhost与127.0.0.1绑定在了一起。
IPv6
IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址。
IPv6的地址长度为128位(8个无符号整数),是IPv4地址长度的4倍。
Java Object/**
* ip地址
*/
@Test
public void inetAddressTest() throws UnknownHostException {
System.out.println(InetAddress.getLocalHost());// PC-20220209TGKW/192.168.254.1
System.out.println(InetAddress.getLocalHost().getHostName());// PC-20220209TGKW
System.out.println(InetAddress.getLocalHost().getHostAddress());// 192.168.254.1
System.out.println(InetAddress.getLocalHost().getCanonicalHostName());// PC-20220209TGKW
System.out.println();
System.out.println(InetAddress.getByName("127.0.0.1"));// /127.0.0.1
System.out.println(InetAddress.getByName("127.0.0.1").getHostName());// 127.0.0.1
System.out.println(InetAddress.getByName("127.0.0.1").getHostAddress());// 127.0.0.1
System.out.println(InetAddress.getByName("127.0.0.1").getCanonicalHostName());// 127.0.0.1
System.out.println();
System.out.println(InetAddress.getByName("localhost"));// localhost/127.0.0.1
System.out.println(InetAddress.getByName("localhost").getHostName());// localhost
System.out.println(InetAddress.getByName("localhost").getHostAddress());// 127.0.0.1
System.out.println(InetAddress.getByName("localhost").getCanonicalHostName());// 127.0.0.1
System.out.println();
System.out.println(InetAddress.getByName("www.baidu.com"));// www.baidu.com/39.156.66.14
System.out.println(InetAddress.getByName("www.baidu.com").getHostName());// www.baidu.com
System.out.println(InetAddress.getByName("www.baidu.com").getHostAddress());// 39.156.66.14
System.out.println(InetAddress.getByName("www.baidu.com").getCanonicalHostName());// 39.156.66.14
}
所谓的端口,就好像是门牌号一样,客户端可以通过ip地址找到对应的服务器端,但是服务器端是有很多端口的,每个应用程序对应一个端口号,通过类似门牌号的端口号,客户端才能真正的访问到该服务器。为了对端口进行区分,将每个端口进行了编号,这就是端口号。
在网络技术中,端口包括逻辑端口和物理端口两种类型。物理端口是用于连接物理设备之间的接口,如ADSL Modem、集线器、交换机、路由器上用于连接其他网络设备的接口,如RJ-45端口、SC端口等等。逻辑端口是指逻辑意义上用于区分服务的端口,比如用于浏览网页服务的80端口,用于FTP服务的21端口等。如TCP/IP协议中的服务端口,通过不同的逻辑端口来区分不同的服务。一个IP地址的端口通过16bit进行编号,最多可以有65536个端口 。端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535。
公认端口
从0到1023,它们紧密绑定(binding)于一些服务,已被占用。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。
注册端口
从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
动态和/或私有端口
从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。但也有例外:SUN的RPC端口从32768开始。
TCP与UDP段结构中端口地址都是16比特,可以有在 0---65535 范围内的端口号。对于这65536个端口号有以下的使用规定:
windows 端口命令
# 查看所有端口
netstat -ano
# 查看指定端口,示例:4092
netstat -ano | findstr "4092"
# 查看指定端口的进程,示例:微信
tasklist | findstr "4092"
Java Object套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。
套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的lP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。例如:如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)。
/**
* IP地址:端口号
*/
@Test
public void inetSocketAddressTest() {
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 4092);
InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost", 4092);
System.out.println(inetSocketAddress);///127.0.0.1:4092
System.out.println(inetSocketAddress2);//localhost/127.0.0.1:4092
System.out.println();
System.out.println(inetSocketAddress.getAddress());///127.0.0.1
//获取ip地址
System.out.println(inetSocketAddress.getHostName());//127.0.0.1
//获取ip地址(字符串)
System.out.println(inetSocketAddress.getHostString());//127.0.0.1
//获取端口
System.out.println(inetSocketAddress.getPort());//127.0.0.1
}
协议:在传输数据的过程中需要遵守的规则。
开放系统互联协议中最早的协议之一,它为连接不同操作系统和不同硬件体系结构的互联网络提供通信支持,是一种网络通用语言。TCP/IP协议定义了在互联网络中如何传递、管理信息(文件传送、收发电子邮件、远程登录等),并制定了在出错时必须遵循的规则。
通讯协议又称通信规程,是指通信双方对数据传送控制的一种约定。约定中包括对数据格式,同步方式,传送速度,传送步骤,检纠错方式以及控制字符定义等问题做出统一规定,通信双方必须共同遵守,它也叫做链路控制规程。
TCP/IP是网络中使用的基本的通信协议。虽然从名字上看TCP/IP包括两个协议,传输控制协议(TCP)和网际协议(IP),但TCP/IP实际上是一组协议,它包括上百个各种功能的协议,如:远程登录、文件传输和电子邮件等,而TCP协议和IP协议是保证数据完整传输的两个基本的重要协议。通常说TCP/IP是Internet协议族,而不单单是TCP和IP。我们通常称它为TCP/IP协议族。
之所以说TCP/IP是一个协议族,是因为TCP/IP协议包括 TCP、IP、UDP、ICMP、RIP、TELNETFTP、SMTP、ARP、TFTP等许多协议,这些协议一起称为TCP/IP协议。
TCP:传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。IP:网际互连协议,Internet Protocol的缩写,是TCP/IP体系中的网络层协议。UDP:用户数据报协议,为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情。面向连接的是 TCP,该协议几乎做了所有的事情。
当一台计算机想要与另一台计算机通讯时,两台计算机之间的通信需要畅通且可靠,这样才能保证正确收发数据。例如,当你想查看网页或查看电子邮件时,希望完整且按顺序查看网页,而不丢失任何内容。当你下载文件时,希望获得的是完整的文件,而不仅仅是文件的一部分,因为如果数据丢失或乱序,都不是你希望得到的结果,于是就用到了TCP。
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。
| TCP | UDP | |
|---|---|---|
| 是否连接 | 面向连接 | 无连接 |
| 是否可靠 | 可靠连接,使用流量控制和拥塞控制 | 不可靠传输,不使用流量控制和拥塞控制 |
| 连接对象个数 | 只能是一对一通信 | 支持一对一,一对多,多对多交互通信 |
| 传输方式 | 面向字节流 | 面向报文 |
| 首部开销 | 首部最小20个字节,最大60个字节 | 首部开销小,仅8个字节 |
| 适用场景 | 适用于要求可靠传输的应用,例如文件传输 | 适用于实时应用(IP电话、视频会议、直播等) |
第一次握手:客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。
客户端---->服务端:客户端向服务端请求连接
第二次握手:服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。
客户端<----服务端:服务端告诉客户端可以连接
第三次握手:当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。
客户端---->服务端:客户端直接连接服务端
为什么 TCP 建立连接需要三次握手,而不是两次?
这是因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。
TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK。
ServerSocketaccept/**
* 数据传输服务端
*/
@Test
public void serverTest() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8080);
System.out.println("##########服务端启动完成!");
Socket socket = null;
InputStream is = null;
OutputStream os = null;
while (true) {
try {
System.out.println("##########等待连接~~~");
socket = serverSocket.accept();
System.out.println(String.format("##########已接收到客户端的连接,IP:%s,端口:%d", socket.getInetAddress().getHostName(), socket.getPort()));
is = socket.getInputStream();
//接收客户端消息
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
if ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println("接收到数据: " + baos.toString());
//响应客户端消息
os = socket.getOutputStream();
os.write("叽里呱啦哒哒哒~~~".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
System.out.println("##########已关闭客户端连接");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket/**
* 数据传输客户端
*/
@Test
public void clientTest() {
Socket socket = null;
OutputStream os = null;
InputStream is = null;
try {
//连接服务端
socket = new Socket("127.0.0.1", 8080);
//向服务端发送消息
os = socket.getOutputStream();
os.write("你好,我是客户端,来聊五毛钱的~~".getBytes());
//接收服务端消息
is = socket.getInputStream();
/*
StringBuilder sb = new StringBuilder();
int len;
byte[] bytes = new byte[1024];
if ((len = is.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len));
}
System.out.println("客户端接收到数据: " + sb.toString());
*/
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
if ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 文件上传服务端
*/
@Test
public void uploadServerTest() throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
System.out.println("等待客户端连接......");
Socket socket = serverSocket.accept();
System.out.println("接收到文件~~~");
FileOutputStream fos = new FileOutputStream(new File("D:\\" + System.currentTimeMillis() + ".png"));
InputStream is = socket.getInputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
System.out.println(StringUtils.getUUID());
fos.write(buffer, 0, len);
}
System.out.println("----------------------------------");
OutputStream ops = socket.getOutputStream();
ops.write("服务端接收成功~~~".getBytes());
ops.close();
is.close();
fos.close();
socket.close();
System.out.println("接收文件成功~~~");
}
}
/**
* 文件上传客户端
*/
@Test
public void uploadClientTest() throws IOException {
Socket socket = new Socket("localhost", 8080);
OutputStream ops = socket.getOutputStream();
FileInputStream fis = new FileInputStream("C:\\uploadFileTest.png");
int len;
byte[] buffer = new byte[1024];
while ((len = fis.read(buffer)) != -1) {
ops.write(buffer, 0, len);
}
//通知服务器,文件发送完毕
socket.shutdownOutput();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = socket.getInputStream();
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
is.close();
fis.close();
ops.close();
socket.close();
}
客户端,如果不加
socket.shutdownOutput();就会阻塞。因为服务器会一直等待客户端的输出。既然服务器阻塞了,客户端等待着服务器的输出,也会被阻塞,所以导致客户端和服务端都被阻塞。调用Socket.shutdownOutput()方法后,客户端输出的数据都将被发送,并加上 TCP 的正常连接终止序列(-1,也就是服务端终止循环的判断条件),这样服务端读取数据时就不会被阻塞了。
想必socket.close()也做了类似的事情。
但是本代码需要接收服务器的响应,所以不能关闭socket,只能单向关闭客户端输出流。
/**
* UDP实现消息发送(接收端)
*/
@Test
public void udpReceiverTest() throws IOException {
//开放端口
DatagramSocket socket = new DatagramSocket(8080);
while (true) {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//阻塞接收
socket.receive(packet);
//System.out.println(JSON.toJSONString(packet));
String msg = new String(packet.getData(), 0, packet.getLength());
System.out.println(msg);
if ("88".equals(msg)) {
break;
}
}
socket.close();
}
/**
* UDP实现消息发送(发送端)
*/
@Test
public void udpSenderTest() throws IOException {
//建立一个 Socket
DatagramSocket socket = new DatagramSocket();
//准备数据:控制台输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
//创建数据包
String msg = br.readLine();
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("127.0.0.1"), 8080);
//发送数据包
socket.send(packet);
if ("88".equals(msg)) {
break;
}
}
br.close();
//关闭连接
socket.close();
}
public class Sender implements Runnable {
DatagramSocket socket;
BufferedReader br;
String toIp;
int toPort;
public Sender(int fromPort, String toIp, int toPort) {
this.toIp = toIp;
this.toPort = toPort;
try {
socket = new DatagramSocket(fromPort);
} catch (SocketException e) {
e.printStackTrace();
}
br = new BufferedReader(new InputStreamReader(System.in));
}
@Override
public void run() {
while (true) {
String msg = null;
try {
//创建数据包
msg = br.readLine();
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName(toIp), toPort);
//发送数据包
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
if ("88".equals(msg)) {
break;
}
}
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
//关闭连接
socket.close();
}
}
public class Receiver implements Runnable {
private DatagramSocket socket;
private final String fromName;
public Receiver(int localPort, String fromName) {
this.fromName = fromName;
try {
//开放端口
socket = new DatagramSocket(localPort);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
try {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//阻塞接收
socket.receive(packet);
//System.out.println(JSON.toJSONString(packet));
String msg = new String(packet.getData(), 0, packet.getLength());
System.out.println(fromName + ":" + msg);
if ("88".equals(msg)) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
/**
* @author caoguangcong
* @date 2022/10/8 23:20
*/
public class UdpChatTest {
@Test
public void talkStudentTest() {
new Thread(new Receiver(8888, "李老师")).start();
new Thread(new Sender(8881, "127.0.0.1", 7777)).start();
while (true) {
//防止jvm停止
}
}
@Test
public void talkTeacherTest() {
new Thread(new Receiver(7777, "张三(学生)")).start();
new Thread(new Sender(7771, "127.0.0.1", 8888)).start();
while (true) {
//防止jvm停止
}
}
}
@Test
public void urlTest() throws IOException {
URL url = new URL("http://localhost:8080/projectName/index.jsp?username=zhangsan&password=123123");
//协议
System.out.println(url.getProtocol());
//主机ip
System.out.println(url.getHost());
//端口号
System.out.println(url.getPort());
//协议默认端口号
System.out.println(url.getDefaultPort());
//文件路径
System.out.println(url.getPath());
//文件全路径
System.out.println(url.getFile());
//参数
System.out.println(url.getQuery());
}
http
localhost
8080
80
/projectName/index.jsp
/projectName/index.jsp?username=zhangsan&password=123123
username=zhangsan&password=123123
tomcat 服务器webapps目录下创建文件:aladdin;aladdin 文件夹下新建一个 txt 格式文件:demo.txt,内容随便书写;apache-tomcat-9.0.29\bin\startup.bat,启动 tomcat;demo;file.txt,下载成功,内容与 demo.txt 内容一致。@Test
public void url2Test() throws IOException {
URL url = new URL("http://localhost:8080/aladdin/demo.txt");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream is = connection.getInputStream();
FileOutputStream fos = new FileOutputStream("file.txt");
int len;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
is.close();
connection.disconnect();
}
/*
问题1:
我们获取的InputStream,可以吧请求信息利用read方法一个一节一个字节的读取出来
但是众多的请求信息中,我们只要第一行的 GET /day19/web/index.html HTTP/1.1
又但是,InputStream中没有一次读取一行的方法,BufferedReader中有readLine方法
所以,我们就想办法能不能把InputStream转成BufferedReader?
解决:
BufferedReader(Reader in)
问题2:
a.我们创建BufferedReader的时候,构造方法的参数,是一个Reader(字符流),但是我们想要将字节流转成BufferedReader
参数不匹配,不匹配就不能转,所以我们现在要考虑的问题是如何将
->字节流转成字符流(换句话说,只要能将InputStream放到BufferedReader的构造参数上就哦了)
解决问题2:
a. BufferedReader(Reader in)->Reader是一个 抽象类->FileReader
b. 由于Reader是一个父类,所以其实我们BufferedReader的参数位置随意传,只要是Reader的子类就行
c. InputStreamReader是Reader的子类,所以可以传递到BufferedReader构造参数位置
d. InputStreamReader类的构造方法InputStreamReader(InputStream in)
e.总结:我们可以利用转换流将InputStream转成BufferedReader对象
new BufferedReader(new InputStreamReader(is))
BufferedReader
特有的方法:
String readLine()
构造:
BufferedReader(Reader in)
*/
public class Server {
public static void main(String[] args)throws Exception {
//1.创建ServerSocket对象,指定端口号
ServerSocket serverSocket = new ServerSocket(8888);
while(true){
//2.获取客户端对象
Socket socket = serverSocket.accept();
//3.调用getInputStream获取浏览器发过来的请求
InputStream is = socket.getInputStream();
//4.创建BufferedReader对象
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();// GET /day14/web/index.html HTTP/1.1
//5.处理字符串想办法得到day14/web/index.html
String[] split = s.split(" ");
String s1 = split[1];// /day14/web/index.html
String path = s1.substring(1);// day14/web/index.html
//6.创建FileInputStream读取本地的页面
FileInputStream fis = new FileInputStream(path);
//7.调用getOutputStream,往客户端响应页面
OutputStream os = socket.getOutputStream();
os.write("HTTP/1.1 200 OK\r\n".getBytes());//响应行
os.write("Content-Type:text/html\r\n".getBytes());//响应头
os.write("\r\n".getBytes());
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
//关流
os.close();
fis.close();
br.close();
is.close();
socket.close();
}
}
}
public class Server_Thread {
public static void main(String[] args)throws Exception {
//1.创建ServerSocket对象,指定端口号
ServerSocket serverSocket = new ServerSocket(8888);
while(true){
//2.获取客户端对象
Socket socket = serverSocket.accept();
new Thread(()->{
try{
//3.调用getInputStream获取浏览器发过来的请求
InputStream is = socket.getInputStream();
//4.创建BufferedReader对象
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();// GET /day14/web/index.html HTTP/1.1
//5.处理字符串想办法得到day14/web/index.html
String[] split = s.split(" ");
String s1 = split[1];// /day14/web/index.html
String path = s1.substring(1);// day14/web/index.html
//6.创建FileInputStream读取本地的页面
FileInputStream fis = new FileInputStream(path);
//7.调用getOutputStream,往客户端响应页面
OutputStream os = socket.getOutputStream();
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
//关流
os.close();
fis.close();
br.close();
is.close();
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}).start();
}
}
}
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。
我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一
使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案
我正在开发一个xcode自动构建系统。在执行一些预构建验证时,我想检查指定的证书文件是否已被撤销。我了解securityverify-cert验证其他证书属性但不验证吊销。我如何检查撤销?我正在用Ruby编写构建系统,但我对任何语言的想法都持开放态度。我阅读了这个答案(Openssl-Howtocheckifacertificateisrevokedornot),但指向底部的链接(DoesOpenSSLautomaticallyhandleCRLs(CertificateRevocationLists)now?)进入的Material对我的目的来说有点过于复杂(用户上传已撤销的证书是一
是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案