网络编程
- 计算机网络
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。 - 网络编程
就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。
网络模型
计算机网络之间以何种规则进行通信,就是网络模型研究问题。
- OSI(Open System Interconnection开放系统互连)七层参考模型
- TCP/IP四层参考模型
主机至网络层(物理层 , 数据链路层) , 网际层 , 传输层 , 应用层(应用层 , 表示层 , 会话层)
网络编程三要素
IP地址
IP地址:InetAddress: 网络中设备的标识,不易记忆,可用主机名。
所谓IP地址就是给每个连接在Internet上的主机分配的一个32bit地址。
- IP地址 = 网络地址+主机地址
A类IP地址:第一段号码为网络地址,剩下的三段号码为本地计算机的号码
B类IP地址:前二段号码为网络地址,剩下的二段号码为本地计算机的号码
C类IP地址:前三段号码为网络地址,剩下的一段号码为本地计算机的号码 - 特殊地址
127.0.0.1 回环地址,可用于测试本机的网络是否有问题. ping 127.0.0.1
DOS命令 ipconfig:查看本机IP地址
xxx.xxx.xxx.255 广播地址
为了方便我们对IP地址的获取和操作,java提供了一个类 InetAddress 供我们使用
此类表示互联网协议 (IP) 地址。
- InetAddress类的常见功能
方法 | 功能 |
---|---|
public static InetAddress getByName(String host) | 确定主机名称的IP地址 |
public String getHostAddress() | 返回文本显示中的IP地址字符串 |
public String getHostName() | 获取此IP地址的主机名 |
static InetAddress getLocalHost() | 返回本地主机的地址 |
public class IP_Address {
public static void main(String[] args) throws UnknownHostException {
InetAddress localHost = InetAddress.getLocalHost();
String hostName = localHost.getHostName();
String hostAddress = localHost.getHostAddress();
InetAddress byName = InetAddress.getByName(hostName);
System.out.println(hostName);
System.out.println(hostAddress);
System.out.println(byName);
}
}
输出结果:
端口
- 物理端口 网卡口
- 逻辑端口 我们指的就是逻辑端口
端口就是用来实现一个计算机上的多个网络程序并发运行,在同一个计算机中每个程序对应唯一的端口,这样一个计算机上就可以通过端口区分发送给每个端口的数据。
- 每个网络程序都会有一个逻辑端口。
- 用于标识进程的逻辑地址,不同进程的标识。
- 有效端口:0-65535(两个字节),其中0~1023系统使用或保留端口。
协议
- UDP
将数据源和目的封装成数据包中,不需要建立连接
每个数据报的大小在限制在64k
因无连接,是不可靠协议
不需要建立连接,速度快 - TCP
建立连接,形成传输数据的通道
在连接中进行大数据量传输
需要连接所以是可靠协议
必须建立连接,效率会稍低
通过网络编程实现网络传输,三要素缺一不可,客户端(Client)须了解服务端(Server)的IP地址和与客户端相匹配的端口才能实现网络数据传输。我们就根据两类网络协议来分别介绍两种网络编程方式。
UDP编程
UDP协议数据传输
Java中实现UDP协议数据传输的类为DatagramSocket和DatagramPacket。
实现步骤:
- 服务端
- 创建UDP通讯协议服务器端对象,要用有参数构造指定端口号
- 创建数据报包用来接收数据
public DatagramPacket(byte[] buf, int length)
- 接收数据
receive(dp);
(receive()是一个阻塞方法,运行后阻塞,直到接收到客户端的数据继续运行) - 解析数据报包,拿出数据
dp.getData() ; dp.getLength() ;
- 释放资源
- 客户端
- 创建UDP通讯客户端对象
- 创建数据报包
public DatagramPacket(byte[] buf, int length, InetAddress address,int port)
- 发送数据
- 释放资源
代码实现:
- 服务端接收数据
public class UDP_Server {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(6767);
System.out.println("服务器已经开启,等待连接...");
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
socket.receive(packet);
byte[] data = packet.getData();
int length = packet.getLength();
String address = packet.getAddress().getHostAddress();
System.out.println(new String(data, 0, length));
}
}
receive()是一个阻塞方法,运行后阻塞,直到接收到客户端的数据继续运行。
- 客户端发送数据
public class UDP_Client {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
byte[] bytes="Hello World".getBytes();
InetAddress byName = InetAddress.getByName("192.168.11.88");
DatagramPacket packet = new DatagramPacket(bytes,bytes.length,byName,6767);
socket.send(packet);
socket.close();
}
}
运行客户端后服务端接收完成运行结束。
UDP模拟聊天室程序
既然实现了UDP传输数据,那么也可以轻松实现传输客户端键盘录入数据,并反复传输给服务端,再通过多线程实现接受发送双向并发运行,来模拟聊天室程序。
public class UDP_Chat {
public static void main(String[] args) {
new Thread(() -> {
try {
DatagramSocket socket = new DatagramSocket(6767);
System.out.println("服务器已经开启,等待连接...");
while (true){
DatagramPacket packet = new DatagramPacket(new byte[1024],1024);
socket.receive(packet);
byte[] data=packet.getData();
int length = packet.getLength();
String address = packet.getAddress().getHostAddress();
System.err.println(new String(data,0,length));
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
try {
DatagramSocket socket = new DatagramSocket();
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()){
String s = sc.nextLine();
if (s.equals("exit")) break;
byte[] bytes=s.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.11.40"), 7676);
socket.send(packet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
只需要与另一台主机互相匹配IP地址与端口号就可以功能实现。但是UDP协议传输实际并不需要建立连接,并且数据未发送成功的情况下不会返回异常,会导致数据丢失,所以UDP协议本质上并不符合实时聊天程序的出发点,所以实现实时数据反复传输更适合通过TCP协议。
TCP编程
UDP协议数据传输
Java中实现UDP协议数据传输的类为Socket和ServerSocket。
- 服务端接收数据
- 创建TCP通讯协议服务器端对象(ServerSocket)
- 监听客户端
- 获取输入流对象
- 读取数据
- 释放资源
public class TCP_Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(6767);
System.out.println("服务器已经开启,等待连接...");
Socket accept = serverSocket.accept();
InputStream in = accept.getInputStream();
byte[] bytes = new byte[1024];
int len=in.read(bytes);
System.out.println(new String(bytes,0,len));
}
}
- 客户端发送数据
- 创建TCP通讯协议客户端对象(Socket)
- 获取输出流对象
- 写数据
- 释放资源
public class TCP_Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("192.168.11.88",6767);
OutputStream out = socket.getOutputStream();
out.write("Hello World".getBytes());
socket.close();
}
}
TCP模拟聊天室程序
public class TCP_Chat {
public static void main(String[] args) {
new Thread(() -> {
try {
ServerSocket socket = new ServerSocket(6677);
System.out.println("已开启......");
while (true){
Socket accept = socket.accept();
InputStream in = accept.getInputStream();
byte[] bytes = new byte[1024];
int len = in.read(bytes);
System.err.println(new String(bytes,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
try {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()){
String s = sc.next();
Socket socket = new Socket("192.168.11.88",7676);
OutputStream out = socket.getOutputStream();
out.write(s.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP发送文件
通过服务端对象和客户端对象获取字节IO流可以实现文件的传输。(可以通过第二个端口出书文件名实现复制路径)
- 服务端接收文件
public class RecieveFile {
public static void main(String[] args) throws IOException {
ServerSocket namesocket = new ServerSocket(6677);
System.out.println("服务器已经开启,等待连接...");
Socket nameaccept = namesocket.accept();
InputStream in1 = nameaccept.getInputStream();
byte[] bytes1 = new byte[1024];
int len1=in1.read(bytes1);
String string = new String(bytes1, 0, len1);//拿到文件名
namesocket.close();
FileOutputStream out = new FileOutputStream(string+"1");
ServerSocket filesocket = new ServerSocket(6767);
Socket fileaccept = filesocket.accept();
InputStream in2 = fileaccept.getInputStream();
byte[] bytes2 = new byte[1024*8];
int len2=0;
while ((len2=in2.read(bytes2))!=-1){
out.write(bytes2,0,len2);
}
filesocket.close();
}
}
- 客户端发送文件
public class SendFile {
public static void main(String[] args) throws IOException {
File file = new File("串烧.mp3");
Socket namesocket = new Socket("192.168.11.88",6677);
String s=file.getName();
OutputStream out1 = namesocket.getOutputStream();
out1.write(s.getBytes());
namesocket.close();
FileInputStream in = new FileInputStream(file);
Socket filesocket = new Socket("192.168.11.88",6767);
OutputStream out2 = filesocket.getOutputStream();
int len=0;
byte[] bytes = new byte[1024 * 8];
while ((len=in.read(bytes))!=-1){
out2.write(bytes);
}
filesocket.close();
in.close();
}
}