网络编程
一、网络编程的概述
1、网络编程就是实现网络互连的不同计算机上运行的程序的数据交换。
2、计算机要进行信息传输就需要有一定的规则,计算机网络通信的这些规则是网络模型研究的问题,一般有两种网络模型:OSI(Open SystemInterconnection开放系统互连)参考模型和TCP/IP参考模型,如下图:
3.网络编程一般针对网络层和传输层进行。网络层主要将从下层接收到的数据进行IP地址的封装与解封装,在这一层工作的设备是路由器,常把这一层的数据叫做数据包; 传输层定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据,如上传下载),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。传输层主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组,常常把这一层数据叫做段。
二、网络通信三要素
1、IP地址:InetAddress,网络中设备的标识,不易记忆,可用主机名;
要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接受数据的计算机和识别发送的计算机,在TCP/IP协议中,这个标识号就是IP地址。
3、传输协议:即通讯的规则,常见协议:TCP,UDP。
UDP:将数据源和目的封装成数据包中,不需要建立连接,每个数据报的大小在限制在64k,因无连接,是不可靠协议,不需要建立连接,速度快;
TCP:建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接,效率会稍低。
三、网络编程常用类
(1)InetAddress类:
常用功能:a.获取任意主机:getByName;b.获取主机名:getHostName;c.获取主机Ip地址:getHostAddress;
(2)Socket类:
Socket套接字:网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字;
Socket原理机制:通信的两端都有Socket;网络通信其实就是Socket间的通信;数据在两个Socket间通过IO传输。
Socket机制图解:
(3)UDP相关类:DatagramSocket与DatagramPacket;
建立发送端,接收端。DatagramSocket;
建立数据包。DatagramPacket;
调用Socket的发送接收方法。send()和receive()方法;receive()方法阻塞是方法.没数据就等着
关闭Socket。close()方法;
(4)TCP相关类:Socket和ServerSocket;
建立客户端(Socket)和服务器端(ServerSocket);
建立连接后,通过Socket中的IO流进行数据的传输。getOutputStream()和getInputStream()方法
关闭socket。close()方法
三、网络编程的应用
1、UDP传输(模拟收发短信息)
发送端思路:
1:建立udp的socket服务;
2:将要发送的数据封装成数据包;
3:通过udp的socket服务,将数据包发送出;
4:关闭资源。
【案例——发送端】
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* 数据来自于键盘录入
* 键盘录入数据要自己控制录入结束。
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
// 创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket();
// 封装键盘录入数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine()) != null) {
if ("886".equals(line)) {
break;
}
// 创建数据并打包
byte[] bys = line.getBytes();
/* DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("192.168.1.108"), 10086);*/
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("192.168.1.108"), 10086);
// 发送数据
ds.send(dp);
}
// 释放资源
ds.close();
}
}
接收端思路:
1:建立udp的socket服务;
2:通过receive方法接收数据;
3:将收到的数据存储到数据包对象中;
【案例——接收端】
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
* 本程序能看到:多次启动接收端会出现端口被占用异常
* java.net.BindException: Address already in use: Cannot bind
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(10086);
while (true) {
// 创建一个包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收数据
ds.receive(dp);
// 解析数据
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println("from " + ip + " data is : " + s);
}
// 释放资源
// 接收端应该一直开着等待接收数据,是不需要关闭
// ds.close();
}
}
2、TCP传输(模拟上传文件)
客户端思路:
1:建立客户端的Socket服务,并明确要连接的服务器;
2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入,该通道称为Socket流,Socket流中既有读取流,也有写入流;
3:通过Socket对象的方法,可以获取这两个流;
4:通过流的对象可以对数据进行传输;
5:如果传输数据完毕,关闭资源。
【案例——客户端】
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class UploadClient {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket s = new Socket("192.168.1.108", 10010);
// 封装文本文件
BufferedReader br = new BufferedReader(new FileReader(
new File("F:\\答辩PPT.ppt")));
// 封装通道内流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了
s.shutdownOutput();
// 接收反馈
BufferedReader brClient = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String client = brClient.readLine(); // 阻塞
System.out.println(client);
// 释放资源
br.close();
s.close();
}
}
【案例——服务器】
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
public static void main(String[] args) throws IOException {
// 创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(10010);
// 监听客户端连接
Socket s = ss.accept();// 阻塞
// 封装通道内的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封装文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
"F:\\copy答辩.ppt")));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// 给出反馈
BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
bwServer.write("文件上传成功");
bwServer.newLine();
bwServer.flush();
// 释放资源
bw.close();
s.close();
}
}
小结:
1、服务器端还可以用while循环来接收多个客户端的数据,但这种方式存在缺点,就是所有客户端只能上传同一类型的文件,并且所有客户端上传的数据都是保存在服务器下同一个文件中,但这种缺点可以通过多线程来克服。
2、客户端连接上服务端,两端都在等待,没有任何数据传输,因为read方法或者readLine方法是阻塞式。使用shutdownInput,shutdownOutput方法能很好的解决这问题