文章目录
第五章 网络编程
1.IP地址
每个设备都必须拥有一个独特的地址,用以标示其在网络上的位置
1.1 IP地址的组成
有32位,由4个8位二进制数组成,唯一标识网络上的每一台计算机
IP地址 = 网络地址 +主机地址
网络地址:标识计算机或网络设备所在的网段
主机地址:标识特定主机或网络设备
1.2 IP地址的配置和检测
查看IP地址,检测网络是否畅通:
- 查看本机的IP地址:
ipconfig
- 测试网络是否通畅:
ping 目标IP地址
1.3 DNS域名解析
Domain Name System,域名系统
从域名解析出IP地址
2.网络通信协议
为了在网络中不同的计算机之间进行通信而建立的规则、标准或约定的集合
协议 | |
---|---|
应用层 | HTTP、FTP、TFTP、SMTP、SNMP、DNS |
传输层 | TCP、UDP |
网络层 | ICMP、IGMP、IP、ARP、RARP |
数据链路层 | 由底层网络定义的协议 |
物理层 | 由底层网络定义的协议 |
物理层: 基于电器特性的高低电压(电信号),高电压代表1,低电压代表0
数据链路层: 定义电信号的分组方式
网络层: 引入一套新的地址来区分不同的广播域
传输层: 端到端的连接通信
应用层(应用层,表示层,会话层): 规定了数据的传输格式
3.Socket
通信链路的端点就被称为“套接字”(英文名Socket)
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是提供给应用程序的接口
Socket的底层机制复杂,Java平台提供了一些简单的API,可以更简单有效的使用Socket开发而无需了解底层机制
java.net包:
Socket
ServerSocket
DatagramPacket
DatagramSocket
InetAddress
3.1 基于TCP协议的Socket编程:
-
基于TCP协议的Socket网络通信
用来实现双向安全连接网络通信
-
Socket通信模型
进行网络通信时,Socket需要借助数据流I/O来完成数据的传递工作
三次握手四次挥手 A(客户端)B(服务端)
三次握手(建立连接)
1.(A->B)携带syn数据包 A问B:你能收到我的消息吗?
2.(B->A)如果B同意连接则回复消息同意,向A发送syn+ack B回A:我收到了,你能收到我的消息吗?
3.(A->B)A再向B发送ack数据包 A回B:我收到了,开始通信
四次挥手(关闭连接)
1.(A->B)发送fin包 A:我要关闭,等你确认
2.(B->A)发送ack包,此时B会进入等待关闭状态 B:稍等,等待关闭
3.(B->A)发送fin包,进入最后确认状态 B:确认完成,可以关闭
4.(A->B)A收到后回复ack包,进入超时等待状态。等待结束关闭A,B收到ack后立即关闭连接 A确认关闭,B立即关闭
步骤:
建立连接->打开Socket关联的输入输出流->数据流中读写信息->关闭所有的数据流和Socket
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。
客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。
客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
客户端:
1.socket()函数
2.bind()函数可有可无,加上指定传输端口,不加随机分配端口;
3.connect()函数,填写服务端的地址与端口【网络间通信AF_STREAM】或者路径【进程间通信AF_DGRAM】
4.send()函数;
5.recv()函数。
服务端:
1.socket()函数;
2.bind()函数,必须加上指定传输端口【网络间通信AF_STREAM】或者是路径【进程间通信AF_DGRAM】 ;
3.listen()函数,使用isockfd;
4.accepc()函数,生成新的fd,inewfd;
5.send()函数,inewfd;
6.recv()函数,inewfd;
服务器端实现步骤:
- 建立连接,监听端口。
- 使用accept()方法等待客户端触发通信。
- 打开Socket关联的输入/输出流。
- 向输出流中写入信息。
- 从输入流中读取响应信息。
- 关闭所有的数据流和Socket。
package ch08.com.hz.sktest;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
public class Service {
public static void main(String[] args) {
Socket socket = null;
InputStream is = null;
OutputStream os = null;
ServerSocket serverSocket = null;
try {
// 1.建立连接,监听端口。
serverSocket = new ServerSocket(6001);
System.out.println("服务器已启动...");
// 2.使用accept()方法等待客户端触发通信。
socket = serverSocket.accept();
// 3.打开Socket关联的输入/输出流(需要先转换为字符流)
is = socket.getInputStream();
os = socket.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 4.向输出流中写入信息
String str = br.readLine();
System.out.println("我是服务器,客户端输入的消息为:" + str);
// 对客户端输入信息进行处理
String[] str2 = str.split("&");
HashMap<String, String> hm = new HashMap<>();
for (String str1 : str2) {
String[] str3 = str1.split(":");
hm.put(str3[0], str3[1]);
}
String reply = "登录失败";
if ("admin".equals(hm.get("name"))
&& "123456".equals(hm.get("password"))) {
reply = "登录成功!";
}
// 5.从输入流中读取响应信息
os.write(reply.getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 6.关闭所有的数据流和Socket。
os.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端实现步骤:
- 建立连接,监听端口。
- 打开Socket关联的输入/输出流。
- 向输出流中写入信息。
- 从输入流中读取响应信息。
- 关闭所有的数据流和Socket。
package ch08.com.hz.sktest;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
Socket socket = null;
BufferedReader bis = null;
try {
//1.建立连接,连接指向服务器及端口。
socket = new Socket("127.0.0.1",6001);
//2.打开Socket关联的输入/输出流。
os = socket.getOutputStream();
is = socket.getInputStream();
//3.向输出流中写入信息。向服务器发送消息,需要先转换为字节流
Scanner can = new Scanner(System.in);
System.out.println("输入账号:");
String name = can.next();
System.out.println("输入密码:");
String password = can.next();
String str = "name:" + name + "&password:" + password;
byte[] b = str.getBytes();
os.write(b);
socket.shutdownOutput();
//4.从输入流中读取响应信息.从服务器读取信息。需要先把字节流转换为容易读取的字符流
bis = new BufferedReader(new InputStreamReader(is));
String str2 = bis.readLine();
System.out.println("客户端输出:服务器回应为==>" + str2);
socket.shutdownInput();
}catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//5.关闭所有的数据流和Socket。
bis.close();
os.close();
is.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
类型:
(1)流式套接字(SOCK_STREAM)
提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收(TCP协议)
(2)数据报式套接字(SOCK_DGRAM)(多线程、循环监听)
提供无连接服务,数据包以独立包形式发送,不提供无措保证,数据可能丢失,并且接收顺序混乱(UDP协议)
(3)原始套接字(SOCK_RAM)
Socket中实现对象的传递
对象要序列化
对象:
public class User implements Serializable {}
服务器:
ObjectInputStream ois = new ObjectInputStream(is);
User user = (User) ois.readObject();
客户端:
User user = new User(name, password);
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
多线程处理多请求(while循环)
一个专门负责监听的应用主服务程序
一个专门负责处理请求的线程程序
采用多线程的方式
3.2 基于UDP协议的Socket编程
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |
创建DatagramSocket对象:
//创建一个数据包套接字,绑定到本机上任意一个可用的端口
DatagramSocket();
//创建一个绑定的数据报套接字, 与指定的datagramsocketimpl相关(一般不使用)
DatagramSocket(DatagramSocketImpl);
//通过制定的套接字地址来创建一个数据包套接字
DatagramSocket(SocketAddress);
//指定本地的一个端口,以此来创建数据包套接字
DatagramSocket(int);
//创建一个DatagramSocket对象,并绑定到指定的地址和端口上
DatagramSocket(int, InetAddress);
接收和发送数据:
//发送数据
public void send(DatagramPacket p) throws IOException {.....}
//接收数据
public synchronized void receive(DatagramPacket p) throws IOException {....}
相关方法:
// 返回绑定到本机套接字的端口
int getLocalPort();
//返回套接字的连接地址
InetAddress getInetAddress();
//关闭该数据报套接字
void close();
步骤:
- 利用 DatagramPacket 对象封装数据包
- 利用 DatagramSocket 发送数据包
- 利用 DatagramSocket 接收数据包
- 利用 DatagramPacket 处理数据包
package ch08.com.hz.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
//客户
public class Client {
public static void main(String[] args) {
DatagramSocket datagramSocket = null;
DatagramPacket datagramPacket = null;
try {
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",8888);
datagramSocket = new DatagramSocket(7777);
byte[] b = "客服你好".getBytes();
//利用 DatagramPacket 对象封装数据包
datagramPacket = new DatagramPacket(b,b.length,isa);
//利用 DatagramSocket 发送数据包
datagramSocket.send(datagramPacket);
//利用 DatagramSocket 接收数据包
System.out.println("接收客服回复...");
datagramSocket.receive(datagramPacket);
//利用 DatagramPacket 处理数据包
byte[] bytes = datagramPacket.getData();
int len = datagramPacket.getLength();
System.out.println(new String(bytes,0,len));
} catch (Exception e) {
e.printStackTrace();
}finally {
datagramSocket.close();
}
}
}
package ch08.com.hz.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
//客服
public class Server {
public static void main(String[] args) {
DatagramSocket datagramSocket = null;
DatagramPacket datagramPacket = null;
try {
System.out.println("客服在线...");
//构建服务器+端口
datagramSocket = new DatagramSocket(8888);
//封装成包 DatagramPacket(byte[] buf,int lenth)
byte[] bytes = new byte[1024];
datagramPacket = new DatagramPacket(bytes, bytes.length);
//接收客户端发来的数据
datagramSocket.receive(datagramPacket);
//获取接收到的数据
byte[] b = datagramPacket.getData();
//获取接收到的数据的长度
int len = datagramPacket.getLength();
//输出
System.out.println(new String(b,0,len));
System.out.println("客服回复中...");
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",7777);
//回复客户
String msg = "客户你好";
//利用 DatagramPacket 对象封装数据包
datagramPacket = new DatagramPacket(msg.getBytes(),msg.getBytes().length,isa);
//利用 DatagramSocket 发送数据包
datagramSocket.send(datagramPacket);
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭资源
datagramSocket.close();
}
}
}