网络编程概述
Java是Internet 上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。
Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在Java 的本机安装系统里,由JVM 进行控制。并且Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
计算机网络:
- 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
网络编程的目的:
- 直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯。
网络编程中有两个主要的问题:
- 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
- 找到主机后如何可靠高效地进行数据传输
网络通信要素概述
通信双方地址
一定的规则(即:网络通信协议。有两套参考模型)
- OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
网络编程中两个主要的问题:
- 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
- 找到主机后如何可靠高效地进行数据传输
- 提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)
通信要素1:IP和端口号
IP的理解与InetAddress类的实例化
IP 地址:InetAddress
-
唯一的标识Internet 上的计算机(通信实体)
-
本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
-
IP地址分类方式1:IPV4和IPV6
- IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1
- IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
-
IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)。192.168.开头的就是私有地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用
-
特点:不易记忆
-
Internet上的主机有两种方式表示地址:
-
InetAddress类主要表示IP地址,两个子类:Inet4Address、Inet6Address。
-
InetAddress类对象含有一个Internet主机地址的域名和IP地址:www.atguigu.com和202.108.35.210。
-
域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。-------域名解析
-
实例化InetAddress的常用方法
变量和类型 |
方法 |
描述 |
static InetAddress |
getByName(String host) |
根据主机名称确定主机的IP地址。 |
String |
getHostAddress() |
返回文本表示中的IP地址字符串。 |
String |
getHostName() |
获取此IP地址的主机名。 |
static InetAddress |
getLocalHost() |
返回本地主机的地址。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.net.InetAddress; import java.net.UnknownHostException;
public class InetAddressTest { public static void main(String[] args) { try { InetAddress inet1 = InetAddress.getByName("192.168.1.1"); InetAddress inet2 = InetAddress.getByName("www.baidu.com"); InetAddress inet3 = InetAddress.getLocalHost(); System.out.println("inet1:" + inet1); System.out.println("inet2:" + inet2); System.out.println("inet3:" + inet3); String hostName = inet2.getHostName(); String hostAddress = inet2.getHostAddress(); System.out.println("inet2对应的主机名是" + hostName); System.out.println("inet2对应的IP地址是" + hostAddress); } catch (UnknownHostException e) { e.printStackTrace(); } } }
|
端口号的理解
- 端口号标识正在计算机上运行的进程(程序)
- 不同的进程有不同的端口号
- 被规定为一个16 位的整数0~65535。
- 端口分类:
- 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23)
- 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
- 动态/私有端口:49152~65535。
- 端口号与IP地址的组合得出一个网络套接字:Socket。
通信要素2:网络协议
网络通信协议
- 计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
问题:网络协议太复杂
- 计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?
通信协议分层的思想
- 在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
TCP和UDP网络通信协议的对比
传输层协议中有两个非常重要的协议:
- 传输控制协议TCP(Transmission Control Protocol)
- 用户数据报协议UDP(User Datagram Protocol)。
TCP/IP 以其两个主要协议:传输控制协议(TCP)和网络互联协议(IP)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
IP(Internet Protocol)协议是网络层的主要协议,支持网间互连的数据通信。
TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层、IP层、传输层和应用层。
TCP协议:
- 使用TCP协议前,须先建立TCP连接,形成传输数据通道
- 传输前,采用“三次握手”方式,点对点通信,是可靠的
- TCP协议进行通信的两个应用进程:客户端、服务端。
- 在连接中可进行大数据量的传输传输完毕,需释放已建立的连接,效率低
UDP协议:
- 将数据、源、目的封装成数据包,不需要建立连接
- 每个数据报的大小限制在64K内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送
- 发送数据结束时无需释放资源,开销小,速度快
TCP网络编程
例一
实现TCP网络编程,客户端向服务端发送信息,服务端将信息输出到控制台上
注意:先启动服务端,再启动客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TCPTest { @Test public void client() { InetAddress inet = InetAddress.getByName("127.0.0.1");
Socket socket = new Socket(inet, 2077); OutputStream os = socket.getOutputStream(); os.write("你好,这里是客户端".getBytes()); os.close(); socket.close(); }
@Test public void server() { ServerSocket ss = new ServerSocket(2077); Socket socket = ss.accept(); InputStream os = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[5]; int len; while ((len = os.read(buffer)) != -1) { baos.write(buffer, 0, len); }
System.out.println(baos); System.out.println("收到了来自于:"+socket.getInetAddress().getHostName()+"的信息"); baos.close(); os.close(); socket.close(); ss.close(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TCPTest { @Test public void client() { Socket socket = null; OutputStream os = null; try { InetAddress inet = InetAddress.getByName("127.0.0.1"); socket = new Socket(inet, 2077); os = socket.getOutputStream(); os.write("你好,这里是客户端".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void server() { ServerSocket ss = null; Socket socket = null; InputStream os = null; ByteArrayOutputStream baos = null; try { ss = new ServerSocket(2077); socket = ss.accept(); os = socket.getInputStream(); baos = new ByteArrayOutputStream(); byte[] buffer = new byte[5]; int len; while ((len = os.read(buffer)) != -1) { baos.write(buffer, 0, len); } System.out.println(baos); System.out.println("收到了来自于:"+socket.getInetAddress().getHostName()+"的信息"); } catch (IOException e) { e.printStackTrace(); } finally { if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (ss != null) { try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
|
例二
客户端发送图片给服务端,服务端将文件保存在本地。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import org.junit.Test;
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TCPTest02 { @Test public void client() { Socket socket = new Socket(InetAddress.getLocalHost(), 2077); OutputStream os = socket.getOutputStream(); FileInputStream fis = new FileInputStream("不可以涩涩.png");
byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } fis.close(); os.close(); socket.close(); }
@Test public void server() { ServerSocket ss = new ServerSocket(2077); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); FileOutputStream fos = new FileOutputStream("可以涩涩.png"); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } System.out.println("涩图接收完毕!"); fos.close(); is.close(); socket.close(); ss.close(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| import org.junit.Test;
import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TCPTest02 { @Test public void client() { Socket socket = null; OutputStream os = null; FileInputStream fis = null; try { socket = new Socket(InetAddress.getLocalHost(), 2077); os = socket.getOutputStream(); fis = new FileInputStream("不可以涩涩.png");
byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (os != null) try { os.close(); } catch (IOException e) { e.printStackTrace(); } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void server() { ServerSocket ss = null; Socket socket = null; InputStream is = null; FileOutputStream fos = null; try { ss = new ServerSocket(2077); socket = ss.accept(); is = socket.getInputStream(); fos = new FileOutputStream("可以涩涩.png"); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } System.out.println("涩图接收完毕!"); } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (ss != null) { try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
|
例三
在例二的基础上,服务端给客服端一个反馈,表明服务端已经成功接收涩图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| import org.junit.Test;
import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TCPTest03 { @Test public void client() { Socket socket = null; OutputStream os = null; FileInputStream fis = null; InputStream is = null; try { socket = new Socket(InetAddress.getLocalHost(), 2042); os = socket.getOutputStream(); fis = new FileInputStream("不可以涩涩.png"); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } socket.shutdownOutput(); is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer1 = new byte[20]; int len1; while ((len1 = is.read(buffer1)) != -1){ baos.write(buffer1,0,len1); } System.out.println(baos); } catch (IOException e) { e.printStackTrace(); } finally { if (is!=null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (os != null) try { os.close(); } catch (IOException e) { e.printStackTrace(); } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void server() { ServerSocket ss = null; Socket socket = null; InputStream is = null; FileOutputStream fos = null; OutputStream os = null; try { ss = new ServerSocket(2042); socket = ss.accept(); is = socket.getInputStream(); fos = new FileOutputStream("可以涩涩1.png"); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } System.out.println("涩图接收完毕!"); os = socket.getOutputStream(); os.write("你好,我是服务器,涩图接收完毕,蟹蟹~".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if (os!=null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (ss != null) { try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
|
例四
服务端向客户端发送涩图,客户端成功接收后,向服务端反馈
这里为了方便理解代码的实现,采用的是抛异常处理,正常还应该是用try/catch/finally来处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| import org.junit.Test;
import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TCPTest04 { @Test public void client() throws Exception { Socket socket = new Socket(InetAddress.getLocalHost(), 4396); InputStream is = socket.getInputStream(); byte[] buffer = new byte[1024]; FileOutputStream fos = new FileOutputStream("我要涩涩.png"); int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } System.out.println("涩图接收完毕"); OutputStream os = socket.getOutputStream(); os.write("服务端你好,我是客户端,感谢你发来的涩图,我已成功接收".getBytes()); os.close(); fos.close(); is.close(); socket.close(); }
@Test public void server() throws Exception { ServerSocket ss = new ServerSocket(4396); Socket socket = ss.accept(); OutputStream os = socket.getOutputStream(); FileInputStream fis = new FileInputStream("不可以涩涩.png"); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } socket.shutdownOutput(); InputStream is = socket.getInputStream(); byte[] buffer1 = new byte[5]; int len1; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((len1 = is.read(buffer1)) != -1) { baos.write(buffer1, 0, len1); } System.out.println(baos); baos.close(); fis.close(); os.close(); socket.close(); ss.close(); } }
|
例五
客户端给服务端发文本,服务端将文本转化为大写再返回客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TCTTest05 { @Test public void client() throws Exception { Socket socket = new Socket(InetAddress.getLocalHost(), 9527); OutputStream os = socket.getOutputStream(); os.write("The worst way to miss someone is to be sitting right beside her knowing you can't have her.".getBytes()); socket.shutdownOutput(); InputStream is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[5]; int len; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } System.out.println(baos); is.close(); os.close(); socket.close(); }
@Test public void server() throws Exception { ServerSocket ss = new ServerSocket(9527); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[5]; int len; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } String str = baos.toString().toUpperCase(); OutputStream os = socket.getOutputStream(); os.write(str.getBytes()); os.close(); baos.close(); is.close(); socket.close(); ss.close(); } }
|
UDP网络编程
类DatagramSocket和DatagramPacket实现了基于UDP 协议网络程序。
UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。如同发快递包裹一样。
流程:
- DatagramSocket与DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用Socket的发送、接收方法
- 关闭Socket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import org.junit.Test;
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;
public class UDPTest { @Test public void sender() throws Exception { DatagramSocket socket = new DatagramSocket(); String str = "我是UDP方式发送的导弹"; byte[] data = str.getBytes(); DatagramPacket packet = new DatagramPacket(data, 0, data.length, InetAddress.getLocalHost(), 9421); socket.send(packet); socket.close(); }
@Test public void receiver() throws Exception { DatagramSocket socket = new DatagramSocket(9421); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(), 0, packet.getLength())); socket.close(); } }
|
URL网络编程
URL的理解与实例化
-
URL:统一资源定位符,对应着互联网的某一资源地址
-
格式:
http://127.0.0.1:8080/work/164.jpg?username=subei
协议 主机名 端口号 资源地址 参数列表
-
常用方法
变量和类型 |
方法 |
描述 |
String |
getFile() |
获取此 URL的文件名。 |
String |
getHost() |
获取此 URL的主机名(如果适用)。 |
String |
getPath() |
获取此 URL的路径部分。 |
int |
getPort() |
获取此 URL的端口号。 |
String |
getProtocol() |
获取此 URL的协议名称。 |
String |
getQuery() |
获取此 URL的查询部分。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import java.net.MalformedURLException; import java.net.URL;
public class URLTest { public static void main(String[] args) { try { URL url = new URL("http://127.0.0.1:8080/work/164.jpg?username=subei"); System.out.println("文件名:"+url.getFile()); System.out.println("主机名:"+url.getHost()); System.out.println("路径:"+url.getPath()); System.out.println("端口号:"+url.getPort()); System.out.println("协议:"+url.getProtocol()); System.out.println("查询名"+url.getQuery()); } catch (MalformedURLException e) { e.printStackTrace(); } }
}
|