前言
网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据。程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就是狭义的网络编程范畴。在发送和接收数据时,大部分的程序设计语言都设计了专门的API实现这些功能,程序员只需要调用即可。
一、网络编程概述
网络编程分为服务端和客户端。服务端就相当于我们平时说的服务器,有固定的IP地址,随时等待服务器连接并做出响应;而客户端相当于各种端系统,找到服务端进行连接获取服务。
二、网络通信协议与接口
1.网络通信协议
TCP/IP协议: 传输控制协议/因特网互联协议(Transmission Control Protocol/Internet Protocal),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
2.网络通信接口
为了使两个节点之间能进行对话,必须在他们之间建立通信工具(即接口),使彼此之间,能进行信息交换。接口包括两部分:
- 硬件装置:实现结点之间的信息传送
- 软件装置:规定双方进行通信的约定协议
3.网络分层模型
OSI参考模型: 模型过于理想化,未能在因特网上进行广泛推广
TCP/IP参考模型(或TCP/IP协议): 事实上的国际标准。
模型如下(图示例):
OSI七层网络模型 | TCP/IP四层概念模型 | 对应网络协议 |
---|
应用层(Application)
|
应用层
|
HTTP、TFTP、FTP、NFS、WAIS、SMTP
|
表示层(Presentation)
|
Telnet、Rlogin、SNMP、Gopher
|
会话层(Session)
|
SMTP、DNS
|
传输层(Transport)
|
传输层
|
TCP、UDP
|
网络层(Network)
|
网络层
|
IP、ICMP、ARP、RARP、AKP、UUCP
|
数据链路层(Data Link)
|
数据链路层
|
FDDI、Ethernet、Arpanet、PDN、SLIP、PPP
|
物理层(Physical)
|
IEEE 802.1A、IEEE 802.2到IEEE 802.11
|
上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能。
- 应用层: 主要负责应用程序的协议,例如HTTP协议、FTP协议等。
- 传输层: 主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
- 网络层: 网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
- 数据链路层: 链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
三、网络协议
1.IP地址
在网络中每台计算机都必须有一个的IP地址。每个人的电脑都有一个独一无二的IP地址,这样互相通信时就不会传错信息了。
- 127.0.0.1: 本机回环地址
- IPV4: 32为地址,以点分十进制表示,如192.168.0.1
- IPV6: 128位(16个字节)写成8个16位的无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
| IPV4 | IPV6 |
---|
地址长度 | IPv4协议具有32位(4字节)地址长度 | IPv6协议具有128位(16字节)地址长度 | 格式 | IPv4 地址的文本格式为 nnn.nnn.nnn.nnn,其中 0<=nnn<=255,而每个 n 都是十进制数。可省略前导零 | IPv6 地址的文本格式为 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx,其中每个 x 都是十六进制数可省略前导零 | 数量 | 共有43亿,30亿在北美,4亿在亚洲,2011年就已经用尽 | 多到每一粒沙子都可以分配一个IPv6地址 |
代码如下(示例):
public class Test {
public static void main(String[] args) throws UnknownHostException {
InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress1);
InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress2);
InetAddress inetAddress3 = InetAddress.getLocalHost();
System.out.println(inetAddress3);
System.out.println(inetAddress2.getAddress());
System.out.println(inetAddress2.getCanonicalHostName());
System.out.println(inetAddress2.getHostAddress());
System.out.println(inetAddress2.getHostName());
}
}
2.端口(port)
端口表示计算机上的一个程序的进程。
IP地址用来标识一台计算机,但是一台计算机上可能提供多种网络应用程序,如何来区分这些不同的程序呢?这就要用到端口。端口号是用来区分一台机器上不同的应用程序的。
记住一点,我们编写的程序要占用端口号的话占用1024以上的端口号,1024以下的端口号不要去占用,因为系统有可能会随时征用。端口号本身又分为TCP端口和UDP端口,TCP的8888端口和UDP的8888端口是完全不同的两个端口。TCP端口和UDP端口都有65536个。
- 不同进程监听不同的端口
- 端口号0~65535
- UDP和TCP协议端口号独立,可以同时有UDP和TCP的80端口
windows 命名提示符:
netstat -ano
netstat -ano | findstr "XXXXX"
tasklist | findstr "XXXX"
代码如下(示例):
public class TestInetSocketAddress {
public static void main(String[] args) {
InetSocketAddress socketAddress1 = new InetSocketAddress("localhost",8080);
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080);
System.out.println(socketAddress);
System.out.println(socketAddress1);
System.out.println(socketAddress.getAddress());
System.out.println(socketAddress.getHostName());
System.out.println(socketAddress.getPort());
}
}
四、网络通信协议
1.TCP协议
TCP:传输控制协议 (Transmission Control Protocol)。 TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
三次握手: TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
- 第一次: 客户端向服务器端发出连接请求,等待服务器确认。
- 第二次: 服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次: 客户端再次向服务器端发送确认信息,确认连接。
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
四次挥手: 四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
TCP实现步骤:
- 客户端
- 服务端
- 建立服务端口ServerSocket
- 等待用户的连接accept
- 接受用户的消息
Socket实现TCP
代码如下(示例):
public class Test {
public static void main(String[] args) throws IOException {
InetAddress sAddress = InetAddress.getByName("127.0.0.1");
int port = 9999;
Socket socket = new Socket(sAddress,port);
OutputStream os = socket.getOutputStream();
os.write("hello你好!".getBytes(StandardCharsets.UTF_8));
os.close();
socket.close();
}
}
public class Test {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
if (baos!=null){
baos.close();
}
if (is!=null){
is.close();
}
if (socket!=null) {
socket.close();
}
if (serverSocket!=null) {
serverSocket.close();
}
}
}
TCP文件上传
客户端:
public class Test {
public static void main(String[] args) throws IOException {
InetAddress ServerIp = InetAddress.getByName("127.0.0.1");
int port = 9999;
Socket socket = new Socket(ServerIp, port);
OutputStream outputStream = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("hello.jpg"));
byte[] buffer = new byte[1024];
int len;
while((len=fis.read())!=-1){
outputStream.write(buffer,0,len);
}
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte [] buffer2 = new byte[2014];
int len2;
while((len2=inputStream.read(buffer2))!=-1){
baos.write(buffer2,0,len2);
}
System.out.println(baos.toString());
if(outputStream!=null){
outputStream.close();
}
if (socket!=null){
socket.close();
}
}
}
服务端:
public class Test {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream(new File("re.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
OutputStream os = socket.getOutputStream();
os.write("我接受完毕了".getBytes(StandardCharsets.UTF_8));
if (fos!=null){
fos.close();
}
if (is!=null){
is.close();
}
if (socket!=null) {
socket.close();
}
if (serverSocket!=null) {
serverSocket.close();
}
}
}
2.UDP协议
UDP:用户数据报协议(User Datagram Protocol)。 UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时,不建议使用UDP协议。
特点: 数据被限制在64kb以内,超出这个范围就不能发送了。
Socket实现UDP
代码如下(示例):
public class Test {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
String msg = "hello,你好服务器!";
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 9090;
DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,address,port);
socket.send(packet);
socket.close();
}
}
public class Test {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(9090);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
socket.receive(packet);
System.out.println(new String(packet.getData()));
socket.close();
}
}
UDP实现聊天室
发送端:
public class UdpSend implements Runnable {
DatagramSocket socket = null;
BufferedReader reader = null;
private int fromPort;
private String toIP;
private int toPort;
public UdpSend(int fromPort,String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try {
socket = new DatagramSocket(fromPort);
reader = new BufferedReader(new InputStreamReader(System.in));
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
String str = reader.readLine();
DatagramPacket packet = new DatagramPacket(str.getBytes(),0,
str.getBytes().length, new InetSocketAddress(this.toIP,this.toPort));
socket.send(packet);
if (str.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
接收端:
public class UdpAccept implements Runnable {
DatagramSocket socket = null;
private int port1;
private String msgfrom;
public UdpAccept(int port1,String msgfrom) {
this.port1 = port1;
this.msgfrom = msgfrom;
try {
socket = new DatagramSocket(port1);
} 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);
byte[] data =packet.getData();
String str = new String(data,0,data.length).trim();
System.out.println(msgfrom + ":" + str);
if (str.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
学生
public class Student {
public static void main(String[] args) {
new Thread(new UdpSend(8000,"localhost",9999)).start();
new Thread(new UdpAccept(8888,"老师")).start();
}
}
老师
public class Teacher {
public static void main(String[] args) {
new Thread(new UdpSend(5555,"localhost",8888)).start();
new Thread(new UdpAccept(9999,"学生")).start();
}
}
五、Socket套接字
Socket套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信的五种必须信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的协议端口。
套接字(socket)是一个抽象层,传输层连接的端点叫做套接字(socket)。应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。
JDK中也封装了Socket,TCP协议的实现是Socket和ServerSocket,UDP协议的实现是DatagramSocket。
Java中UDP协议的socket实现是DatagramSocket,并且不区分客户端和服务端。
代码如下(示例):
1.Socket实现TCP协议
服务端:
public class ServerSocketTest {
public static void main(String[] args) throws Exception {
int port = 55533;
ServerSocket server = new ServerSocket(port);
System.out.println("server将一直等待连接的到来");
Socket socket = server.accept();
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len, "UTF-8"));
}
System.out.println("get message from client: " + sb);
inputStream.close();
socket.close();
server.close();
}
}
客户端:
public class ClientSocketTest {
public static void main(String args[]) throws Exception {
String host = "127.0.0.1";
int port = 55533;
Socket socket = new Socket(host, port);
OutputStream outputStream = socket.getOutputStream();
String message = "林深时见鹿";
socket.getOutputStream().write(message.getBytes("UTF-8"));
outputStream.close();
socket.close();
}
}
总结: 这是一个客户端单向发送消息到服务器端的demo,且服务器端是只等待接收一次连接请求。从示例代码中可以看到创建socket需要指的IP地址和端口,发送和接收消息是需要通过输入输出流的。
2.Socket实现UDP协议
服务端
public class DatagramSocketServerTest {
public static void main(String[] args) {
try {
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
DatagramSocket socket = new DatagramSocket(8088);
System.out.println("服务器端正在等待客户端连接...");
socket.receive(packet);
String receiveMsg = new String(packet.getData(), 0, packet.getLength());
System.out.println(packet.getLength());
System.out.println(receiveMsg);
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端
public class DatagramSocketClientTest {
public static void main(String[] args) {
try {
String sendMsg = "客户端发送的消息";
InetAddress addr = InetAddress.getByName("localhost");
DatagramPacket packet = new DatagramPacket(sendMsg.getBytes(), sendMsg.getBytes().length, addr, 8088);
DatagramSocket socket = new DatagramSocket();
socket.send(packet);
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结: 这也是一个客户端单向发送消息到服务器端的demo,且服务器端是只等待接收一次连接请求。从示例代码中可以看到使用DatagramSocket进行通信需要将消息、消息长度和对方地址、端口号封装在DatagramPacket中,DatagramSocket发送出的每一个DatagramPacket都包含了IP地址和端口号。
六、HTTP协议
1.http和https
HTTP (Hyper Text Transfer Protocol),超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。
HTTPS(Hyper Text Transfer Protocol Secure),安全套接字层超文本传输协议,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
- http是HTTP协议运行在TCP之上。所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。
- https是HTTP运行在SSL/TLS之上,SSL/TLS运行在TCP之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。此外客户端可以验证服务器端的身份,如果配置了客户端验证,服务器方也可以验证客户端的身份。
HTTP和HTTPS的区别:
| HTTP | HTTPS |
---|
协议 | 运行在 TCP 之上,明文传输,客户端与服务器端都无法验证对方的身份 | 身披 SSL外壳的 HTTP;运行于 SSL 上,SSL 运行于 TCP 之上, 是添加了加密和认证机制的 HTTP。 | 端口 | 80 | 443 | 资源消耗 | 较少 | 由于加解密处理,会消耗更多的 CPU 和内存资源 | 开销 | 无需证书 | 需要证书,而证书一般需要向认证机构购买 | 加密机制 | 无 | 共享密钥加密和公开密钥加密并用的混合加密机制 | 安全性 | 弱 | 由于加密机制,安全性强 |
2.URL
URL 称为统一资源定位符,用于定位互联网上的某个资源。
- URL对应的path不同,获取到的页面也是不同的
- URL中的服务器的ip来确定一个服务器
- URL中的服务器端口来确定这个主机上的哪个进程
- URL中的path来确定这个进程中所管理的哪个文件/资源
代码如下(示例):
public class URLDemo {
public static void main(String[] args) throws MalformedURLException {
URL ur1 = new URL("http://1oca1host/");
System.out.println(ur1.getProtocol());
System.out.println(url.getHost());
System.out.print1n(url.getPort());
System.out.println(url.getPath());
System.out.print1n(url.getFile());
System.out.println(url.getQuery());
}
}
下载网络资源
public class UrlDown {
public static void main(String[] args) throws IOException {
URL url = new URL("xxxxxxx");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("图标.png");
byte[] buffer = new byte[1024];
int len;
while((len=inputStream.read(buffer))!=-1){
fos.write(buffer,0,len);
}
fos.close();
inputStream.close();
urlConnection.disconnect();
}
}
3.HTTP方法
根据HTTP标准,HTTP请求可以使用多种请求方法。
方法 | 说明 |
---|
GET | 获取资源 | POST | 传输实体主体 | PUT | 传输文件 | DELETE | 删除文件 | OPTIONS | 询问支持的方法 | TRACE | 追踪路径 | LINK | 建立和资源之间的联系 | UNLINK | 断开连接关系 |
4.HTTP状态码
HTTP状态码表示客户端HTTP请求的返回结果、标识服务器处理是否正常、表明请求出现的错误等。
状态码 | 原因 |
---|
1XX | Informational(信息性状态码) 接受的请求正在处理 | 2XX | Success(成功状态码) 请求正常处理完毕 | 3XX | Redirection(重定向状态码) 需要进行附加操作以完成请求 | 4XX | Client Error(客户端错误状态码) 服务器无法处理请求 | 5XX | Server Error(服务器错误状态码) 服务器处理请求出错 |
状态码 | 原因 |
---|
2XX | 成功(这系列表明请求被正常处理了) | 200 | OK,表示从客户端发来的请求在服务器端被正确处理 | 204 | No content,表示请求成功,但响应报文不含实体的主体部分 | 206 | Partial Content,进行范围请求成功 |
状态码 | 原因 |
---|
3XX | 重定向(表明浏览器要执行特殊处理) | 301 | moved permanently,永久性重定向,表示资源已被分配了新的 URL | 302 | found,临时性重定向,表示资源临时被分配了新的 URL | 303 | see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源 | 304 | not modified,表示服务器允许访问资源,但请求未满足条件的情况(与重定向无关) | 307 | temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求 |
状态码 | 原因 |
---|
4XX | 客户端错误 | 400 | bad request,请求报文存在语法错误 | 401 | unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息 | 403 | forbidden,表示对请求资源的访问被服务器拒绝,可在实体主体部分返回原因描述 | 404 | not found,表示在服务器上没有找到请求的资源 |
状态码 | 原因 |
---|
5XX | 服务器错误 | 500 | internal sever error,表示服务器端在执行请求时发生了错误 | 501 | Not Implemented,表示服务器不支持当前请求所需要的某个功能 | 503 | service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求 |
5.GET和POST的区别
HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。
| GET | POST |
---|
浏览器回退/刷新 | 无害 | 数据会再次提交 | 书签 | 可收藏 | 不可收藏 | 缓存 | 浏览器主动缓存 | 不能被缓存,除非手动设置 | 编码类型 | application/x-www-form-urlencoded | 支持多种编码方式 | 数据长度限制 | 有限制的 | 无限制的 | 数据类型限制 | 只允许ASCII字符 | 无限制的,也允许二进制数据 | 历史记录 | 参数会被保留在浏览器历史记录中 | 不会被保留 | 安全性 | 不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。 | 安全,因为是放在Request body中。 | 可见性 | 数据在URL对外可见 | 数据不会显示在URL中 |
总结
本次Java网络编程基础就到这里,看完记得点个赞收藏哦!
|