老曹眼中的网络编程基础
转载自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA
我们很幸运,因为我们有互联网。互联网是一个神奇的东西,它改变了你我的生活方式,也改变了整个世界。然而网络的无标度和小世界特性使其变得复杂、无所不在、无所不能,以至于我们无法区分甚至描述它。
对于一个编码员来说,了解网络的基础知识可能会从理解定义开始,了解OSI七层协议模型,深入到Socket内部,然后才能精通网络编程。
关于网络关于互联网,字典中的定义如下:
在电气系统中,由若干元件组成的、用于按一定要求传输电信号的电路或电路的一部分称为网络。
作为一名一直从事TMN开发的通信专业的我,固执地认为网络是从通信系统中诞生的。沟通是人与人之间通过某种媒介进行信息的交换和传递。传统的通信网络(即电话网)由传输、交换和终端三部分组成。通信网络是指将孤立的设备物理连接起来,实现信息交换的链路,从而达到资源共享和通信的目的。通信网络可以从覆盖范围、拓扑结构、交换方式等多种角度进行分类……我最好把充满回忆的东西留在我的书架上。
互联网的概念外延不断被放大,抽象思维能力是人们创新乃至创造的根源。网络用于表示许多对象及其相互联系,例如数学图、物理模型、交通网络、人际网络、城市网络等。简而言之,网络是用数学图对类似问题进行抽象的概括。表达和研究科学的模型。
很多小伙伴以为明白了这一点,一切就都结束了。我们关心的只是计算机网络。计算机网络是利用通信线路和设备将分布在不同地点的多个计算机系统相互连接起来,按照网络协议共享软硬件功能,最终实现资源共享的系统。特别的,我们所说的网络只是互联网——Internet,或者说移动互联网,需要的是编写互联网应用程序。然而,一位工作了五六年的编程高手曾经对我说,现在他终于明白基础知识有多么重要了。技术在不断发展,而相对不变的是那些原则和编程模型。
资深程序员认为,编程实践是一个螺旋上升的过程,从具体到抽象,再到具体,周而复始。了解前世今生,才能够触及“势”。基础越扎实,建筑就越有想象的空间。对于网络编程的基础知识,您可能应该从OSI 七层协议模型开始。
七层模型
七层模型(OSI,开放系统互连参考模型)是国际标准化组织制定的用于计算机或通信系统之间互连的参考标准体系。它是一个七层抽象模型,不仅包括一系列抽象术语和概念,还包括具体的协议。经典的描述如下:
简单描述一下每一层的含义:
物理层:建立、维护和断开物理连接。
数据链路层(Link):逻辑连接、硬件地址寻址、错误检查等。
网络层(Network):进行逻辑寻址,实现不同网络之间的路径选择。
传输层(Transport):定义传输数据的协议端口号,以及流量控制和错误检查。
会话层:建立、管理和终止会话。
表示层:数据表示、安全性和压缩。
应用层(Application):网络服务和最终用户之间的接口
每一层使用下一层提供的服务与其对等层进行通信,每层都使用自己的协议。了解了这一点后,也就不足为奇了。然而,这个模型确实是大多数网络编程的基础,并且作为一个抽象类而存在,而TCP/IP协议栈只是这个模型的具体实现。
TCP/IP 协议模型
TCP/IP 是互联网的基础,是一组协议的代名词,其中包括构成TCP/IP 协议栈的许多协议。 TCP/IP 有四层模型和五层模型。区别在于数据链路层是否作为独立层存在。我个人更喜欢5层模型,这样二层和三层的交换设备更容易理解。当您谈论网络中的第2 层或第3 层交换机时,您知道您指的是哪些协议。
数据如何传输?这就需要了解网络层和传输层的协议。众所周知的IP数据包结构如下:
IP协议和IP地址是两个不同的概念,这里不涉及IPV6。如果你不关注网络安全,你不必熟悉这些结构。传输层就是利用这样的数据包进行传输,传输层又分为面向连接的可靠传输TCP和数据报UDP。 TCP数据包结构:
建立TCP连接的三次握手绝对是你必须知道的。在调优系统时,内核中的网络相关参数与这张图密切相关。 UDP是一种无连接的传输层协议,提供简单且不可靠的信息传输。协议结构比较简单,包括源端口号、目的端口号、长度、校验和等。基于TCP和UDP的数据封装和解析示例如下:
或者它仍然是一个鸡蛋?如果您了解数据包的大小,您会发现什么?有效负载是多少?在设计协议通信时,这些为我们提供了粒度定义的基础。此外,我们看一个例子。
模型解读示例
FTP 是一个更好的例子。为了方便起见,假设两台计算机是A和B,将使用FTP将A上的文件X传输到B。
首先,计算机A和B之间必须有物理层连接,可以是通过RJ-45电路接口的同轴电缆或双绞线等有线连接,也可以是WIFI等无线连接。我们先简化一下,考虑局域网,暂时不讨论路由器、交换机和WIFI热点。这些物理层连接建立了比特流的原始传输路径。
接下来,数据链路层出现,在两台计算机之间建立数据链路。如果计算机C、D、E等同时连接到A和B所在的网络上,那么A和B之间如何建立数据链路呢?这个过程就是物理寻址。 A需要在众多物理连接中找到B。它依赖于计算机的物理地址,即MAC地址,也就是网卡上的MAC地址。以太网采用CSMA/CD方式传输数据。数据在以太网LAN中以广播方式传输。整个局域网中的所有节点都会收到该帧。只有目标MAC 地址与其自身MAC 地址相同的帧才会被接受。 A通过错误控制和访问控制找到B的网卡,并建立可靠的数据路径。
IP 地址呢?现在数据链路已经建立,我还需要IP地址吗?我们的FTP命令中指定的IP地址不是MAC地址? IP地址是逻辑地址,包括网络地址和主机地址。如果A和B在不同的局域网中,中间有多个路由器,则A需要对B进行逻辑寻址。物理地址用于与底层硬件通信,逻辑地址用于上层协议之间的通信。在以太网中:逻辑地址是IP地址,物理地址是MAC地址。在使用时,两个地址通过一定的算法连接起来。因此,IP就是用来选择网络上的路由的。在FTP命令中,IP中的原始地址是A的IP地址,目的地址是B的IP地址。这应该是网络层,负责将数据包数据从源传输到目的地。
当A向B传输文件时,如果文件中部分数据丢失,可能会导致B无法正常读取或使用。因此,需要可靠的连接来保证传输过程的完整性。这是传输层的TCP协议,FTP是建立在TCP之上的。 TCP的三次握手决定了双方数据包的序列号、最大接收数据大小(窗口)、MSS(最大段大小)。 TCP使用IP来完成寻址。 TCP 提供端口号。 FTP中的目的端口号一般为21。传输层的端口号对应主机进程,指本地主机与远程主机之间正在进行的会话。
会话层用于建立、维护和管理应用程序之间的会话。其主要功能是对话控制和同步。编程中涉及到的会话就是会话层的具体体现。表示层完成数据解码、加解密、压缩解压等,例如FTP中的bin命令代表二进制传输,即传输层数据的格式。 HTTP协议体中的Json、XML等都可以认为是表示层。应用层就是具体的应用程序本身。 FTP中的PUT、GET等命令是应用程序的具体功能特征。
简单来说,物理层连接电缆,数据链路层连接网卡,网络层路由到主机,传输层连接端口,会话层维护会话,表示层表示数据格式,应用层就是FTP中的各种命令函数。
Socket
理解了7层模型之后你会编程吗?你能开始学习编程语言吗?一开始就尝试一下是没问题的。如果你想更进一步,经验丰富的程序员认为最好看看底层的实现,因为一切最终都会归结为系统调用。如何在操作系统层面查看网络?出现套接字。
在Linux世界中,“一切皆文件”。操作系统将网络读写视为IO操作,就像读写文件一样,对外提供的编程接口是Socket。因此,socket是通信的基石,是支持TCP/IP协议网络通信的基本操作单元。套接字本质上为进程通信提供端点。在进程通信之前,双方必须首先创建一个端点,否则将无法相互建立联系和通信。一个完整的套接字有一个本地唯一的套接字编号,该编号由操作系统分配。
从设计模式的角度来看,Socket实际上是一种外观模式,将复杂的TCP/IP协议栈隐藏在Socket接口的背后。对于用户来说,一套简单的Socket接口就足够了。当应用程序创建套接字时,操作系统返回一个整数作为描述符来标识套接字。然后应用程序将描述符作为参数传递并调用函数来完成某些操作(例如通过网络传输数据或接收输入数据)。
在许多操作系统中,Socket 描述符与其他I/O 描述符集成在一起。操作系统将套接字描述符实现为指向内部数据结构的指针数组。进一步来看,操作系统为每个正在运行的进程维护一个单独的文件描述符表。当进程打开一个文件时,系统会将指向文件内部数据结构的指针写入文件描述符表中,并将该表的索引值返回给调用者。
由于Socket与操作系统的IO操作相关,因此各个操作系统IO实现的差异会导致Socket编程时略有差异。如果你在我的Mac 上查看Socket.so,你会发现它与CentOS 上的有些不同。
进程进行Socket操作时,也有多种处理方式,如阻塞IO、非阻塞IO、多路复用(select/poll/epoll)、AIO等。
多路复用通常在提高性能方面发挥着重要作用。 select系统调用的作用是监视多个文件描述符。当带有文件描述符的文件读写操作完成或者发生异常或超时时,调用将返回这些文件描述符。 select需要遍历所有文件描述符。就遍历操作而言,复杂度为O(N)。
epoll相关的系统调用是在Linux 2.5之后的版本中引入的。这个系统调用在设计上做了很大的改变,以解决传统select/poll的缺点。 select/poll的缺点是:
每次调用都需要反复从用户态读取参数并反复扫描文件描述符。
每次调用开始时,当前进程都会被放入每个文件描述符的等待队列中。调用完成后,该进程从各个等待队列中删除。
epoll 将单个select/poll 操作拆分为一个epollcreate、多个epollctrls 和一个wait。另外,操作系统内核还为epoll操作添加了一个文件系统。每个或多个要监控的文件描述符都有一个对应的inode节点,主要信息存储在eventpoll结构中。被监控文件的重要信息存储在epitem结构中,是一对多的关系。由于在执行epollcreate和epollctrl时已经将用户态信息保存到内核中,因此即使之后重复调用epoll_wait,也不会重复复制参数,不会重复扫描文件描述符,也不会重复放置当前进程/从等待队列中取出。
因此,目前主流的服务器端Socket实现大多采用epoll,例如Nginx。可以在配置文件中明确看到使用epoll。
网络编程
了解7层协议模型以及操作系统层面的Socket实现,可以方便我们对网络编程的理解。
在设计系统时,一个重要的部分就是拓扑架构,其中涉及到网络等基础设施。 7层协议的下面四层将帮助我们观察和判断业务系统的网络结构。在设计系统时,往往采用面向接口的设计,接口往往是基于HTTP协议的Restful API。接口的粒度可以以数据段作为约束,同时关注移动互联网的弱网络环境。
不同的编程语言有不同的框架和库。实际编写网络程序代码并不复杂。例如,Erlang中的gen_tcp用于编写一个简单的Echo服务器:
Start_echo_server()-{ok,Listen}=gen_tcp:listen(1234,[binary,{packet,4},{reuseaddr,true},{active,true}]),
{ok,socket}=get_tcp:accept(监听),
gen_tcp:close(听),
循环(套接字)。
循环(套接字)-接收
{tcp,Socket,Bin} -io:format("服务器收到的二进制文件=~p~n",[Bin])
Str=binary_to_term(Bin),
io:format("服务器(已解压)~p~n",[Str]),
回复=lib_misc:string2value(Str),
io:format("服务器回复=~p~n",[回复]),
gen_tcp:send(套接字,term_to_binary(回复)),
循环(套接字);
{tcp_close,Socket} -Io:format("ServerSocket 已关闭~n")
结尾。然而,编写漂亮的服务器程序仍然是一项非常费力的工作。例如,我非常喜欢的Python Tornado代码在ioloop.py中有复用的选项:
@类方法
defconfigurable_default(cls):
如果hasattr(select, "epoll"):
从tornado.platform.epoll导入EPollIOLoop
返回EPollIOLoop
如果hasattr(select, "kqueue"):
# BSD 或Mac 上的Python 2.6+
从tornado.platform.kqueue导入KQueueIOLoop
返回KQueueIOLoop
从tornado.platform.select导入SelectIOLoop
return SelectIOLoop同样继承了HTTPServer.py中的TCPServer,然后实现了HTTP协议。代码片段如下:
HTTPServer 类(TCPServer,可配置,
httputil.HTTPServerConnectionDelegate):
.
def 初始化(自我,request_callback,no_keep_alive=False,io_loop=无,
xheaders=False,ssl_options=无,协议=无,
decompress_request=False,
chunk_size=无,max_header_size=无,
idle_connection_timeout=无,body_timeout=无,
max_body_size=无,max_buffer_size=无):
self.request_callback=request_callback
self.no_keep_alive=no_keep_alive
self.xheaders=xheaders
self.协议=协议
self.conn_params=HTTP1ConnectionParameters(
解压缩=解压缩_请求,
块大小=块大小,
max_header_size=max_header_size,
header_timeout=idle_connection_timeout 或3600,
最大身体尺寸=最大身体尺寸,
body_timeout=body_timeout)
TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,
最大缓冲区大小=最大缓冲区大小,
read_chunk_size=块大小)
self._connections=set()
.
Java网络编程基础
转自并发编程网https://ifeve.com/
Java 网络教程: 基础
Java 提供了非常易于使用的网络API。通过调用这些API,我们可以通过建立TCP/IP 或UDP 套接字轻松地在网络之间进行通信,其中TCP 比UDP 更常用,但我们将在本教程中介绍这两种方法。
网站上还有另外三个Java网络相关的教程,如下:
1.Java IO教程
2.Java NIO教程
3.Java服务器多线程教程(请联系我们参与翻译)
虽然Java网络API允许我们通过socket打开或关闭网络连接,但是所有的网络通信都是基于Java IO类InputStream和OutputStream来实现的。
另外,我们还可以使用Java NIO API中的相关网络类。用法与Java Network API基本类似。 Java NIO API 可以工作在非阻塞模式。在某些特定场景下使用非阻塞模式可以获得更高的性能。推动。
Java TCP网络基础正常情况下,客户端会打开与服务器的TCP/IP 连接,然后客户端开始与服务器进行通信。当通信结束时,客户端关闭连接。流程如下图所示:
[图片上传失败.(image-11fccb-1566521906361)]ClientServer打开连接发送请求接收响应关闭连接
一个客户端可以通过一个打开的连接发送多个请求。事实上,当服务器处于接收状态时,客户端可以发送尽可能多的数据,服务器也可以主动关闭连接。
Java中Socket类和ServerSocket类当客户端想要打开到服务器的TCP/IP 连接时,使用Java Socket 类。 socket类只需要被告知连接的IP地址和TCP端口,其余的都用Java实现。
如果我们想开启一个监听服务来监听客户端对某些指定TCP端口的连接,我们需要使用Java ServerSocket类。当客户端通过Socket连接到服务器的ServerSocket监听时,服务器会为这次连接指定一个Socket。这时客户端与服务器端的通信就变成了Socket与Socket之间的通信。
Socket类和ServerSocket类将在后续文章中详细介绍。
Java UDP网络基础UDP 的工作方式与TCP 略有不同。使用UDP通信时,客户端和服务器之间没有建立连接的概念。服务器可能(也可能不)收到客户端发送给服务器的数据,并且客户端不知道该数据是否被服务器成功接收。服务器向客户端发送数据时也是如此。
由于是不可靠的数据传输,UDP的协议开销比TCP少很多。
在某些场景下,使用无连接UDP比TCP更好,这将在Java UDPDatagramSocket类介绍一文中详细介绍。
当我们想在Java中使用TCP/IP通过网络连接到服务器时,我们需要创建一个java.net.Socket对象并连接到服务器。如果你想使用Java NIO,你也可以在Java NIO中创建一个SocketChannel对象。
Java网络教程之Socket
创建Socket以下示例代码连接到IP 地址为78.64.84.171 的服务器上的端口80。这台服务器就是我们的Web服务器(www.jenkov.com),端口80是Web服务端口。
套接字socket=new Socket("78.46.84.171", 80);我们还可以使用域名代替IP 地址,如下例所示:
Socket socket=new Socket("jenkov.com", 80);Socket发送数据要通过Socket发送数据,我们需要获取Socket的输出流(OutputStream)。示例代码如下:
Socket socket=new Socket("jenkov.com", 80);
OutputStream out=socket.getOutputStream();
out.write("一些数据".getBytes());
出.flush();
关闭();
套接字.close();代码很简单,但是如果想通过网络向服务器发送数据,一定不要忘记调用flush()方法。操作系统底层的TCP/IP实现首先会将数据放入一个更大的数据缓存块中,缓存块的大小适应TCP/IP数据包的大小。 (译者注:调用flush()方法只是将数据写入操作系统缓存,并不能保证数据会立即发送)
Socket读取数据要从Socket读取数据,我们需要获取Socket的输入流(InputStream)。代码如下:
Socket socket=new Socket("jenkov.com", 80);
输入流=socket.getInputStream();
int 数据=in.read();
//.读取更多数据.
附寄();
套接字.close();代码并不复杂,但是需要注意的是,从Socket的输入流中读取数据并不是读取文件。 read() 方法被调用直到返回-1,因为
对Socket而言,只有当服务端关闭连接时,Socket的输入流才会返回-1,而是事实上服务器并不会不停地关闭连接。假设我们想要通过一个连接发送多个请求,那么在这种情况下关闭连接就显得非常愚蠢。 因此,从Socket的输入流中读取数据时我们必须要知道需要读取的字节数,这可以通过让服务器在数据中告知发送了多少字节来实现,也可以采用在数据末尾设置特殊字符标记的方式连实现。关闭Socket当使用完Socket后我们必须将Socket关闭,断开与服务器之间的连接。关闭Socket只需要调用Socket.close()方法即可,代码如下: Socket socket = new Socket("jenkov.com", 80); socket.close();Java 网络教程: ServerSocket 用java.net.ServerSocket实现java服务通过TCP/IP监听客户端连接,你也可以用Java NIO 来代替java网络标准API,这时候需要用到 ServerSocketChannel。创建一个 ServerSocket连接
以下是一个创建ServerSocket类来监听9000端口的一个简单的代码 ServerSocket serverSocket = new ServerSocket(9000);监听请求的连接
要获取请求的连接需要用ServerSocket.accept()方法。该方法返回一个Socket类,该类具有普通java Socket类的所有特性。代码如下: ServerSocket serverSocket = new ServerSocket(9000); boolean isStopped = false;while(!isStopped){ Socket clientSocket = serverSocket.accept(); //do something with clientSocket} 对每个调用了accept()方法的类都只获得一个请求的连接。 另外,请求的连接也只能在线程运行的server中调用了accept()方法之后才能够接受请求。线程运行在server中其它所有的方法上的时候都不能接受客户端的连接请求。所以”接受”请求的线程通常都会把Socket的请求连接放入一个工作线程池中,然后再和客户端连接。更多关于多线程服务端设计的文档请参考 java多线程服务关闭客户端Socket
客户端请求执行完毕,并且不会再有该客户端的其它请求发送过来的时候,就需要关闭Socket连接,这和关闭一个普通的客户端Socket连接一样。如下代码来执行关闭: socket.close();关闭服务端Sockets
要关闭服务的时候需要关掉 ServerSocket连接。通过执行如下代码: serverSocket.close();Java网络编程:UDP DatagramSocket
DatagramSocket类是java通过UDP通信的途径。UDP仍位于IP层的上面。 你可以用DatagramSocket类发送和接收UDP数据包。UDP 和TCPUDP工作方式和TCP有点不同。当你通过TCP发送数据时,你先要创建连接。一旦TCP连接建立了,TCP会保证你的数据传递到对端,否则它将告诉你已发生的错误。 仅仅用UDP来发送数据包(datagrams)到网络间的某个IP地址。你不能保证数据会不会到达。你也不能保证UDP数据包到达接收方的指令。这意味着UDP比TCP有更少的协议开销(无完整检查流)。 当数据传输过程中不在乎数据包是否丢失时,UDP就比较适合这样的数据传输。比如,网上的电视信号的传输。你希望信号到达客户端时尽可能地接近直播。因此,如果丢失一两个画面,你一点都不在乎。你不希望直播延迟,值想确保所有的画面显示在客户端。你宁可跳过丢失的画面,希望一直看到最新的画面。 这种情况也会发生在网上摄像机直播节目中。谁会关心过去发生的什么,你只想显示当前的画面。你不希望比实际情况慢30s结束,只因为你想看到摄像机显示给观众的所有画面。这跟摄像机录像有点不同。从摄像机录制画面到磁盘,你不希望丢失一个画面。你可能还希望有点延迟,如果有重大的情况发生,就不需要倒回去检查画面。通过DatagramSocket发送数据
通过Java的DatagramSocket类发送数据,首先需要创建DatagramPacket。如下: 1buffer = ``newbyte``[``65508``];23InetAddress address = ``newDatagramPacket(buffer, buffer.length, address,``9000``);字节缓冲块(字节数组)就是UDP数据包中用来发送的数据。缓冲块上限长度为65508字节,是单一UDP数据包发送的最大的数据量。 数据包构造函数的长度就是缓存块中用于发送的数据的长度。所有多于最大容量的数据都会被忽略。 包含节点(例如服务器)地址的InetAddress实例携带节点(如服务器)的地址发送的UDP数据包。InetAddress类表示一个ip地址(网络地址)。getByName()方法返回带有一个InetAddress实例,该实例带有匹配主机名的ip地址。 端口参数是UDP端口服务器用来接收正在监听的数据。UDP端口和TCP端口是不一样的。一台电脑同时有不同的进程监听UDP和TCP 80端口。 为了发送数据包,你需要创建DatagramSocket来发送数据。如下: 1DatagramSocketdatagramSocket = ``newDatagramSocket();调用send()方法发送数据,像这样: 1datagramSocket.send(packet);完整示例: 1DatagramSocket datagramSocket = ``newDatagramSocket();23byte``[] buffer = ``"0123456789"``.getBytes();45InetAddress receiverAddress = InetAddress.getLocalHost();67DataframPacket packet = ``newDatagramPacket( buffer, buffer.length, receiverAddress,``80``);8datagramSocket.send(packet);从DatagramSocket获取数据
从DataframSocket获取数据时,首先创建DataframPacket ,然后通过DatagramSocket类的receive()方法接收数据。例如: 1DatagramSocket datagramSocket = ``newDatagramSocket(``80``);23byte``[] buffer = ``newbyte``[``10``];45DatagramPacket packet = ``newDatagramPacket(buffer, buffer.length);67datagramSocket.receive(packet);注意DatagramSocket是如何通过传递参数80到它的构造器初始化的。这个参数是UDP端口的DatagramSocket用来接收UDP数据包的。像之前提到的,TCP和UDP端口是不一样的,也不重叠。你可以有俩个不同的进程同时在端口80监听TCP和UDP,没有任何冲突。 第二,字节缓存块和DatagramPacket创建了。注意DatagramPacket是没有关于节点如何发送数据的信息的,当创建一个方数据的DatagramPacket时,它会直到这个信息。这就是为什么我们会用DatagramPacket接收数据而不是发送数据。因此没有目标地址是必须的。 最后,调用DatagramSocket的receive()方法。直到数据包接收到为止,这个方法都是阻塞的。 接收的数据位于DatagramPacket的字节缓冲块。缓冲块可以通过调用getData()获得: 1byte``[] buffer = packet.getData();缓冲块接收了多少的数据需要你去找出来。你用的协议应该定义每个UDP包发多少数据,活着定义一个你能找到的数据结束标记。 一个真正的服务端程序可能会在一个loop中调用receive()方法,传送所有接收到的DatagramPacket到工作的线程池中,就像TCP服务器处理请求连接一样(查看Java Multithreaded Servers获取更多详情)Java网络教程:URL + URLConnection
HTTP GET和POST从URLs到本地文件在java.net包中包含两个有趣的类:URL类和URLConnection类。这两个类可以用来创建客户端到web服务器(HTTP服务器)的连接。下面是一个简单的代码例子: 1URL url = ``newURL(``"[http://jenkov.com](https://yq.aliyun.com/go/articleRenderRedirect?url=http%3A%2F%2Fjenkov.com%2F)"``);2URLConnection urlConnection = url.openConnection();3InputStream input = urlConnection.getInputStream();4intdata = input.read();5while``(data != -``1``){6System.out.print((``char``) data);7data = input.read();8}9input.close();HTTP GET和POST
默认情况下URLConnection发送一个HTTP GET请求到web服务器。如果你想发送一个HTTP POST请求,要调用URLConnection.setDoOutput(true)方法,如下: 1URL url = ``newURL(``"[http://jenkov.com](https://yq.aliyun.com/go/articleRenderRedirect?url=http%3A%2F%2Fjenkov.com%2F)"``);2URLConnection urlConnection = url.openConnection();3urlConnection.setDoOutput(``true``);一旦你调用了setDoOutput(true),你就可以打开URLConnection的OutputStream,如下: 1OutputStream output = urlConnection.getOutputStream();你可以使用这个OutputStream向相应的HTTP请求中写任何数据,但你要记得将其转换成URL编码(关于URL编码的解释,自行Google)(译者注:具体名字是:application/x-www-form-urlencoded MIME 格式编码)。 当你写完数据的时候要记得关闭OutputStream。从URLs到本地文件
URL也被叫做统一资源定位符。如果你的代码不关心文件是来自网络还是来自本地文件系统,URL类是另外一种打开文件的方式。 下面是一个如何使用URL类打开一个本地文件系统文件的例子: 1URL url = ``newURL(``"file:/c:/data/test.txt"``);2URLConnection urlConnection = url.openConnection();3InputStream input = urlConnection.getInputStream();4intdata = input.read();5while``(data != -``1``){6System.out.print((``char``) data);7data = input.read();8}9input.close();注意:这和通过HTTP访问一个web服务器上的文件的唯一不同处就是URL:”file:/c:/data/test.txt”。 Java网络教程:JarURLConnection Java的JarURLConnection类用来连接Java Jar文件。一旦连接上,你可以获取Jar文件的信息。一个简单的例子如下: 01String urlString = ``"[http://butterfly.jenkov.com/](https://yq.aliyun.com/go/articleRenderRedirect?url=http%3A%2F%2Fbutterfly.jenkov.com%2F)"02+ ``"container/download/"03+ ``"jenkov-butterfly-container-2.9.9-beta.jar"``;0405URL jarUrl = ``newURL(urlString);06JarURLConnection connection = ``newJarURLConnection(jarUrl);0708Manifest manifest = connection.getManifest();0910JarFile jarFile = connection.getJarFile();11//do something with Jar file...Java 网络教程: InetAddress
创建一个 InetAddress 实例InetAddress 的内部方法InetAddress是 Java 对 IP 地址的封装。这个类的实例经常和 UDP DatagramSockets 和 Socket,ServerSocket 类一起使用。创建一个 InetAddress 实例
InetAddress没有公开的构造方法,因此你必须通过一系列静态方法中的某一个来获取它的实例。 下面是为一个域名实例化 InetAddres 类的例子: InetAddress address = InetAddress.getByName("jenkov.com");当然也会有为匹配某个 IP 地址来实例化一个 InetAddress: InetAddress address = InetAddress.getByName("78.46.84.171");另外,它还有通过获取本地 IP 地址的来获取InetAddress的方法(正在运行程序的那台机器) InetAddress address = InetAddress.getLocalHost();InetAddress 内部方法
InetAddress类还拥有大量你可以调用的其它方法。例如:你可以通过调用getAddress()方法来获取 IP 地址的 byte 数组。如果要了解更多的方法,最简单的方式就是读 JavaDoc 文档中关于InetAddress类的部分。Java网络教程:Protocol Design
如果设计一个客户端到服务器的系统,那么同时也需要设计客户端和服务器之间的通信协议。当然,有时候协议已经为你决定好了,比如HTTP、XML_RPC(http response 的 body 使用xml)、或者SOAP(也是http response 的 body 使用xml)。设计客户端到服务端协议的时候,一旦协议决定开启一会儿,来看一些你必须考虑的地方: 1. 客户端到服务端的往返通讯 2.区分请求结束和响应结束。 3.防火墙穿透客户端-服务端往返当客户端和服务端通信,执行操作时,他们在交换信息。比如,客户端执行一个服务请求,服务端尝试完成这个请求,发回响应告诉客户端结果。这种客户端和服务端的信息交换就叫做往返。示意图如下: 当一个计算机(客户端或者服务端)在网络中发送数据到另一个计算机时,从数据发送到另一端接收数据完会花费一定时间。这就是数据在网络间的传送的时间花费。这个时间叫做延迟。 协议中含有越多的往返,协议变得越慢,延迟特别高。HTTP协议只包含一个单独的响应来执行服务。换句话说就是一个单独的往返。另一方面,在一封邮件发送前,SMTP协议包含了几个客户端和服务端的往返。 在协议中有多个往返的原因是:有大量的数据从客户端发送到服务端。这种情况下你有2个选择: 1.在分开往返中发送头信息; 2.将消息分成更小的数据块。 如果服务端能完成头信息的一些初始验证 ,那么分开发送头信息是很明智的。如果头信息是空白的,发送大量数据本身就是浪费资源。 在传输大量数据时,如果网络连接失败了,得从头开始重新发送数据。数据分割发送时,只需要在网络连接失败处重新发送数据块。已经发送成功的数据块不需要重新发送。区分请求结束和响应结束如果协议容许在同一个连接中发送多个请求,需要一个让服务端知道当前请求何时结束、下一个请求何时开始。客户端也需要知道一个响应何时结束了,下一个响应何时开始。 对于请求有2个方法区分结束: 1.在请求的开始处发送请求的字长 2.在请求数据的最后发送一个结束标记。 HTTP用第一个机制。在请求头中 发送了“Content-Length”。请求头会告诉服务端在头文件后有多少字节是属于请求的。 这个模型的优势在于没有请求结束标志的开销。为了避免数据看上去像请求结束标志,也不需要对数据体进行编码。 第一个方法的劣势:在数据传输前,发送者必须知道多少字节数将被传输。如果数据时动态生成的,在发送前,首先你得缓存所有的数据,这样才能计算出数据的字节数。 运用请求结束标志时,不需要知道发送了多少字节数。只需要知道请求结束标志在数据的末尾。当然,必须确认已发送的数据中不包含会导致请求结束标志错误的数据。可以这样做: 可以说请求结束标志是字节值255。当然数据可能包含值255。因此,对数据中包含值255的每一个字节添加一个额外的字节,还有值255。结束请求标志被从字节值255到255之后的值为0。如下编码: 255 in data –>255, 255 end-of-request –>255, 0 这种255,0的序列永远不会出现在数据中,因为你把所有的值255变成了255,255。同时,255,255,0也不会被错认为255,0。255,255被理解成在一起的,0是单独的。防火墙穿透比起HTTP协议,大多数防火墙会拦截所有的其他通信。因此把协议放在HTTP的上层是个好方法,像XML-RPC,SOAP和REST也可以这样做。文章分享结束,Java网络编程与NIO深入解析:基础入门指南和的答案你都知道了吗?欢迎再次光临本站哦!
【Java网络编程与NIO深入解析:基础入门指南】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
我一直在想学习Java网络编程,这个开篇内容刚好可以作为入门指南了!
有6位网友表示赞同!
终于能找到一些关于Java网络编程的基础知识的讲解!太感谢分享了。
有16位网友表示赞同!
NIO概念我一直不太了解,希望能通过这篇文章深入学习一下。
有11位网友表示赞同!
Java网络编程确实是很有用的技能,学习起来会不会比较难呢?
有19位网友表示赞同!
喜欢这类技术细节的解析,希望文章能解释得详细一点,让我更理解NIO的工作原理。
有5位网友表示赞同!
之前看过一些简单网络编程的例子,希望能通过这篇文章了解更精细的操作。
有9位网友表示赞同!
Java开发越来越深入学习了,这个开篇教程正好可以用到实际项目中去锻炼一下。
有7位网友表示赞同!
网络编程确实是一个很广阔的领域,希望这篇文章能给我一些思路和方向。
有13位网友表示赞同!
NIO好像比传统的BIO编程效率更高,真的吗?期待文章讲解具体的性能优势。
有13位网友表示赞同!
最近在项目中遇到一些网络编程的需求,学习这方面知识很有必要!
有10位网友表示赞同!
希望这篇文章能详细讲解Java网络编程过程中有哪些常用的框架和工具。
有10位网友表示赞同!
学习新技能总是充满挑战,但也是乐趣所在!
有19位网友表示赞同!
Java作为热门语言之一,掌握网络编程能力也是很重要的加分项。
有6位网友表示赞同!
期待文章能提供一些实际的代码示例,方便理解和实践。
有17位网友表示赞同!
想了解一下NIO在Java中具体的实现机制,希望能在这篇文章里找到答案!
有10位网友表示赞同!
学习新知识的过程总是充满好奇心,这次将深入探索Java网络编程世界!
有12位网友表示赞同!
通过学习这篇文章,相信我对Java网络编程有更深层的理解。
有8位网友表示赞同!
希望这篇文章能让我对Java网络编程基础有一个清晰的认知。
有20位网友表示赞同!
期待作者能分享一些实战经验和案例分析,让我们更好地掌握Java网络编程技巧!
有20位网友表示赞同!
学习Java一直都是我的目标之一,这次准备从网络编程开始入门!
有15位网友表示赞同!