一、网络编程概述
网络编程就是编写程序使互联网的两个(或多个)设备(如计算机)之间进行数据传输
1.目的:直接或间接的通过网络协议与其他的计算机实现数据的交换,进行通讯。
2.网络编程中要注意的两个问题
①如何准确的定位网络上一台或多台主机,定位主机上的特定的应用
②找到主机后如何可靠的高效的进行数据的传输
二、网络通信中两个关键的要素
要素1、IP和端口号:
①IP:唯一的标识互联网上的计算机(太过于抽象,不容易记忆)
②端口号:定位主机上的某个具体的应用程序(进程)
③IP分类:IPv4 和 IPv6;万维网 和 局域网的区别
④在Java中InetAddress类代表IP
⑤域名:www.baidu.com DNS(域名解析服务器),可以通过域名来访问IP
⑥本地回路地址:127.0.0.1 对应:localhost
端口号:
①不同的进程拥有不同的端口号
②被规定为一个16位的整数0-65535
③端口分类
公认端口:0~1023 被预先定义的服务通信占用(如:HTTP占用的端口80)
注册端口:1024~49151 分配给用户进程或应用程序(如:Tomcat占用端口8080,MySQL占用端口3306)
动态/私有端口:49152~65535
④端口号与IP地址的组合得出一个网络套接字:Socket
要素2、网络通信协议:(TCP/IP参考模型)
概念:
计算机网络中实现通信必须要有一些约束,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准
通信协议分层:物理链路层、IP层、传输层、应用层
传输层协议中有两个重要的协议:
①传输控制协议(TCP)
②用户数据报协议(UDP)
TCP/IP协议:是一组协议由传输控制协议(TCP)和网络互联协议(IP)而得名,包括多个具有不同功能且互为关联的协议。
TCP和UDP的不同
TCP协议:(生活案例:打电话)
1.使用TCP协议前,需要建立TCP连接,形成传输数据通道
2.传输前,采用"三次握手"的方式,点对点通信,是可靠的
3.TCP协议进行通信的两个应用进程:客户端、服务器
4.在连接中可进行大数据量的传输
5.传输完毕,需要释放已建立的连接,效率低
三次握手:
第一步:client 发送 syn 到server 发起握手;
第二步:server 收到 syn后回复syn+ack给client;
第三步:client 收到syn+ack后,回复server一个ack表示收到了server的syn+ack
浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,而后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。
三次握手的原因:防止已经失效的报文发送到服务器端。
四次挥手
第一次挥手:数据传输结束以后,客户端的应用进程发出连接释放报文段,并停止发送数据,其首部:FIN=1,seq=x。
第二次挥手:服务器端收到连接释放报文段之后,发出确认报文,其首部:ack=x+1,seq=z。此时本次连接就进入了半关闭状态,客户端不再向服务器发送数据。而服务器端仍会继续发送。
第三次挥手:若服务器已经没有要向客户端发送的数据,其应用进程就通知服务器释放TCP连接。
第四次挥手:客户端收到连接释放报文段之后,经过2MSL(最长报文端寿命)后,本次TCP连接真正结束,通信双方完成了他们的告别。
UDP协议:(生活案例:发短信)
1.将数据、源、目的封装成数据包,不建立连接
2.每个数据报的大小限制在64k内
3.发送不管对方使用准备好,接收方收到也不确认,所以是不可靠的
4.可以广播发送
5.发送数据结束时无需释放资源,开销小,速度快
如何实现网络中的主机互相通信
1.通信双方的地址(IP和端口号)
2.一定的规则(网络通信协议)
如何实例化InetAddress的两个方法:getByName(),getLocalHost
两个常用方法:
getHostName()
getHostAddress()
public class InetAddressTest {
public static void main(String[] args) {
try {
//实例化
//getAllByName()
InetAddress inet = InetAddress.getByName("192.168.10.100");// --->/192.168.10.100
System.out.println(inet);
InetAddress inet1 = InetAddress.getByName("www.baidu.com");//www.baidu.com/39.156.66.14
System.out.println(inet1);
InetAddress inet2 = InetAddress.getByName("127.0.0.1");// /127.0.0.1
System.out.println(inet2);
//获取本机的IP地址
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);//
//getHostName()
//getHostAddress()
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
三、实现TCP的网络编程
1.客户端发送信息给服务端,服务端将数据显示在控制台上。
注意:先启动服务端,再启动客户端
public class TCPTest {
/*
客户端
*/
@Test
public void client(){
Socket socket = null;
OutputStream ops = null;
try {
//1.实例化对象,指明ip和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet,8899);
//2.获取输出字节流,用于输出数据
ops = socket.getOutputStream();
//3.写出数据
ops.write("你好,我是客户端".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
if (ops!=null){
try {
ops.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;
ByteArrayOutputStream baos = null;
try {
//指明端口号和
//1.实例化服务器端的ServerSocket对象,指明自己的端口号
ss = new ServerSocket(8899);
//2.调用accept(),接收来自客户端的socket
socket = ss.accept();
//3.获取输入流
is = socket.getInputStream();
//过程,不建议,可能会乱码
// byte[] bytes =new byte[1024];
// int len;
// while ((len = is.read(bytes)) != -1){
// String string =new String(bytes,0,len);
// System.out.println(string);
// }
//4.读取输入流中的数据
baos = new ByteArrayOutputStream();
byte[] bytes = new byte[5];
int len;
while ((len = is.read(bytes)) != -1){
baos.write(bytes,0,len);
}
System.out.println(baos.toString());
//收到来自谁的数据,获取客户端的Ip地址
System.out.println("收到了来自:"+socket.getInetAddress().getHostAddress()+"的信息");
} catch (IOException e) {
e.printStackTrace();
} finally {
//5.关闭资源
if (baos!=null){
try {
baos.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();
}
}
}
}
}
效果演示:
2.客户端发送文件给服务端,服务端将文件保存到本地。
应该使用try-catch-finally处理,而不是throws IOException
public class TCPTest1 {
/*
客户端
*/
@Test
public void client() throws IOException {
//1.创建socket对象,指定ip和端口号
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
//2.获取输出流
OutputStream os = socket.getOutputStream();
//3.获取输入流
FileInputStream fis = new FileInputStream(new File("IO流的分类.png"));
//4.具体的读写过程
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1){
os.write(bytes,0,len);
}
//5.关闭资源
fis.close();
os.close();
socket.close();
}
/*
服务端
*/
@Test
public void server() throws IOException {
//1.创建ServerSocket对象,指定自己的端口号
ServerSocket ss = new ServerSocket(9090);
//2.使用accept(),获取客户端的socket
Socket socket = ss.accept();
//3.获取客户端的输入流
InputStream is = socket.getInputStream();
//4.指明输出的位置
FileOutputStream fos = new FileOutputStream(new File("IO流的分类2.png"));
//5.具体的操作过程
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1){
fos.write(bytes,0,len);
}
System.out.println("接收成功");
//6.资源关闭
fos.close();
is.close();
socket.close();
ss.close();
}
}
3.从客户端发送文件给服务端,服务端保存到本地,并返回"发送成功"给客户端
public class TCPTest2 {
/*
客户端
*/
@Test
public void client() throws IOException {
//1.创建socket对象,指定ip和端口号
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
//2.获取输出流
OutputStream os = socket.getOutputStream();
//3.获取输入流
FileInputStream fis = new FileInputStream(new File("IO流的分类.png"));
//4.具体的读写过程
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1){
os.write(bytes,0,len);
}
//关闭数据的输出,服务器就会得到明确的指示,如果没有shutdownOutput(),就会被read()阻塞,执行不了下面的操作
socket.shutdownOutput();
//5.接收来自于服务端的数据,并显示到控制台上
InputStream is = socket.getInputStream();
//为了不乱码,使用ByteArrayOutputStream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes1 = new byte[5];
int len1;
while ((len1 = is.read(bytes1)) != -1){
baos.write(bytes1,0,len1);
}
//显示数据
System.out.println(baos.toString());
//6.关闭资源
fis.close();
os.close();
socket.close();
baos.close();
}
/*
服务端
*/
@Test
public void server() throws IOException {
//1.创建ServerSocket对象,指定自己的端口号
ServerSocket ss = new ServerSocket(9090);
//2.使用accept(),获取客户端的socket
Socket socket = ss.accept();
//3.获取客户端的输入流
InputStream is = socket.getInputStream();
//4.指明输出的位置
FileOutputStream fos = new FileOutputStream(new File("IO流的分类4.png"));
//5.具体的操作过程
byte[] bytes = new byte[1024];
int len;
//read()为阻塞式方法,一直停留在循环中
while ((len = is.read(bytes)) != -1){
fos.write(bytes,0,len);
}
//6.服务端给客户端反馈
System.out.println("发送成功!");
OutputStream os = socket.getOutputStream();
os.write("数据发送成功".getBytes());
//7.资源关闭
fos.close();
is.close();
socket.close();
ss.close();
os.close();
}
}
四、UDP协议的网络编程
/*
发送端
*/
@Test
public void sender() throws IOException {
//1.实例化DatagramSocket
DatagramSocket socket = new DatagramSocket();
//2.数据的声明
String str = "UDP的网络编程";
//3.转换为字节数组
byte[] data = str.getBytes();
//4.指定ip
InetAddress inet = InetAddress.getLocalHost();
//5.实例化DatagramPacket(数据报)
DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
//6.发送数据报
socket.send(packet);
//7.关闭资源
socket.close();
}
/*
接收端
*/
@Test
public void receiver() throws IOException {
//1.实例化DatagramSocket,指定自己的端口号,确定自己的进程
DatagramSocket socket = new DatagramSocket(9090);
//2.数据封装在DatagramPacket,不再发送,不用指定端口号和ip
byte[] bytes = new byte[100];
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length);
//3.接收数据
socket.receive(packet);
//4.输出数据
//packet.getData()获取packet中的数据,从0开始,到哪里结束
System.out.println(new String(packet.getData(),0,packet.getLength()));
//5.资源关闭
socket.close();
}
五、URL网络编程
1.URL(Uniform Resource Locator):统一资源定位符,它表示Internet上的某一资源的地址
2.URL的基本结构有五部分组成:
< 传输协议 >://< 主机名 >:< 端口号 >/< 文件名 >#片段名?参数列表
例:http://192.168.1.100:8080/helloworld/index.jsp?username=admin&password=123456
3.常用方法:
public String getProtocol():获取该URL的协议名
public String getHost():获取该URL的主机名
public String getPort():获取该URL的端口号
public String getPath():获取该URL的的文件路径
public String getFile():获取该URL的文件名
public String getQuery():获取该URL的查询名
public class URLTest {
public static void main(String[] args) {
try {
URL url = new URL("http://192.168.1.100:8080/helloworld/index.jsp?username=admin&password=123456");
System.out.println(url.getProtocol());
System.out.println(url.getHost());
System.out.println(url.getPort());
System.out.println(url.getPath());
System.out.println(url.getFile());
System.out.println(url.getQuery());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
使用URL网络编程在Tomcat中下载文件
public class URLTest1 {
public static void main(String[] args) {
HttpURLConnection urlConnection = null;
InputStream is = null;
FileOutputStream fos = null;
try {
//下载Tomcat服务器中的数据,需要开启Tomcat服务器
URL url =new URL("http://192.168.1.100:8080/helloworld/IO流的分类.png");
//找到连接对象
urlConnection = (HttpURLConnection) url.openConnection();
//获取连接,访问服务器
urlConnection.connect();
//得到一个输入流
is = urlConnection.getInputStream();
//将从服务器得到的数据,写入到具体的文件中,再main方法中,会写入到当前工程下
fos = new FileOutputStream("IO流的分类5.png");
//读写过程
byte[] bytes = new byte[1024];
int len;
//读取文件
while ((len = is.read(bytes)) != -1){
//写入到文件中
fos.write(bytes,0,len);
}
System.out.println("下载完成!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (urlConnection != null){
//断开连接
urlConnection.disconnect();
}
}
}
}