引入
双绞线:
域名:www.baidu.com -------》DNS服务器解析---------》IP地址
设备之间进行传输的时候,必须遵照一定的规则-----》通信协议
TCP/IP
TCP、UDP
可靠的
建立连接:三次握手
释放连接:四次挥手
UDP 不可靠的,包是分开发的
InetAddress、InetSocketAddress类
InetAddress-------》封装了IP
InetSocketAddress----------》封装了IP,端口号
默认修饰符 只能在这个包下使用
public class TestIP {
public static void main(String[] args) throws UnknownHostException {
//封装IP:
//InetAddress ia=new InetAddress();不能直接创建对象,因为InetAddress()被default修饰了 10.170.46.230
InetAddress ia = InetAddress.getByName("10.170.46.230");
System.out.println(ia);
InetAddress ia1 = InetAddress.getByName("localhost");//localhost指代的是本机的ip地址
System.out.println(ia1);
InetAddress ia2 = InetAddress.getByName("127.0.0.1");//127.0.0.1指代的是本机的ip地址
System.out.println(ia2);
InetAddress ia3 = InetAddress.getByName("DESKTOP-MUG7H60");//封装计算机名
System.out.println(ia3);
InetAddress ia4 = InetAddress.getByName("www.mashibing.com");//封装域名
System.out.println(ia4);
System.out.println(ia4.getHostName());//www.mashibing.com获取域名
System.out.println(ia4.getHostAddress());//8.142.18.182获取IP地址
}
}
public class TestIP1 {
public static void main(String[] args) {
InetSocketAddress isa=new InetSocketAddress("10.170.46.230" ,8080);
System.out.println(isa);
System.out.println(isa.getHostName());
System.out.println(isa.getPort());
InetAddress ia = isa.getAddress();//封装成InetAddress对象
System.out.println(ia.getHostName());
System.out.println(ia.getHostAddress());
}
}
网络通信原理—套接字 socket
socket:应用层或取传输层的通信协议
基于TCP的网络编程
功能:模拟网站的登录,客户端录入账号密码,然后服务器端进行验证
功能分解1:单向通信
客户端:
public class TestClient {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的IP和端口号
Socket s=new Socket("10.170.46.230",8888);
//2.对于程序员来说,向外发送数据 感受---》利用输出流
OutputStream os = s.getOutputStream();
DataOutputStream dos=new DataOutputStream(os);
//利用OutputStream就可以向外发送数据,但没有直接发送String的方法
//所以在OutputStream外面套了一个数据流DataOutputStream
dos.writeUTF("你好");
//3.关闭流+关闭网络资源
dos.close();
os.close();
s.close();
}
}
服务器:
public class TestServer {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的端口号
ServerSocket ss=new ServerSocket(8888);
//2.等着客户端发来的信息
Socket s = ss.accept();//阻塞方法,等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行
//accept()返回值为一个socket,这个socket其实就是客户端的socket
//接收到这个socket之后,客户端和服务器才真正产生了链接,才真正可以通信了
//3.创建输入流
InputStream is=s.getInputStream();
DataInputStream dis=new DataInputStream(is);
//4.读取客户端发来的数据
String s1 = dis.readUTF();
System.out.println("客户端发送的:"+s1);
//5.关闭流+关闭网络资源
dis.close();
is.close();
s.close();
ss.close();
}
}
测试:要先开启服务器,再开启客户端
先开客户端出错
功能分解2:双向通信
服务器:
public class TestServer {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的端口号
ServerSocket ss=new ServerSocket(8888);
//2.等着客户端发来的信息
Socket s = ss.accept();//阻塞方法,等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行
//accept()返回值为一个socket,这个socket其实就是客户端的socket
//接收到这个socket之后,客户端和服务器才真正产生了链接,才真正可以通信了
//3.创建输入流
InputStream is=s.getInputStream();
DataInputStream dis=new DataInputStream(is);
//4.读取客户端发来的数据
String s1 = dis.readUTF();
System.out.println("客户端发送的:"+s1);
//像客户端输出一句话----》操作流----《输出流
OutputStream os=s.getOutputStream();
DataOutputStream dos=new DataOutputStream(os);
dos.writeUTF("你好,我是服务器,我接收到你的请求了");
//5.关闭流+关闭网络资源
dos.close();
os.close();
dis.close();
is.close();
s.close();
ss.close();
}
}
客户端:
public class TestClient {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的IP和端口号
Socket s=new Socket("10.170.46.230",8888);
//2.对于程序员来说,向外发送数据 感受---》利用输出流
OutputStream os = s.getOutputStream();
DataOutputStream dos=new DataOutputStream(os);
//利用OutputStream就可以向外发送数据,但没有直接发送String的方法
//所以在OutputStream外面套了一个数据流DataOutputStream
dos.writeUTF("你好");
//接收服务器端的回话-----》操作流----》操作输入流
InputStream is=s.getInputStream();
DataInputStream dis=new DataInputStream(is);
String s1 = dis.readUTF();
System.out.println("服务器对我说:"+s1);
//3.关闭流+关闭网络资源
dis.close();
is.close();
dos.close();
os.close();
s.close();
}
}
注意:关闭防火墙,不然别人访问不到你
功能分解3:对象流传送
用户类:
public class User implements Serializable {//注意序列化,不然后续使用会报错
private static final long serialVersionUID = 4743965775074617135L;
private String id;
private String pwd;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public User(String id, String pwd) {
this.id = id;
this.pwd = pwd;
}
}
客户端:
public class TestClient {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的IP和端口号
Socket s=new Socket("10.170.46.230",8888);
//录入用户的账号和密码
Scanner sc=new Scanner(System.in);
System.out.println("请录入您的账号:");
String id=sc.next();
System.out.println("请录入您的密码:");
String pwd=sc.next();
//将账号和密码封装成一个user类的对象
User user=new User(id,pwd);
//2.对于程序员来说,向外发送数据 感受---》利用输出流
OutputStream os = s.getOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(os);
oos.writeObject(user);
//接收服务器端的回话-----》操作流----》操作输入流
InputStream is=s.getInputStream();
DataInputStream dis=new DataInputStream(is);
boolean b = dis.readBoolean();
if(b){
System.out.println("登录成功!!");
}else{
System.out.println("登陆失败!");
}
//3.关闭流+关闭网络资源
dis.close();
is.close();
oos.close();
os.close();
s.close();
}
}
服务器:
public class TestServer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建套接字:指定服务器的端口号
ServerSocket ss=new ServerSocket(8888);
//2.等着客户端发来的信息
Socket s = ss.accept();//阻塞方法,等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行
//accept()返回值为一个socket,这个socket其实就是客户端的socket
//接收到这个socket之后,客户端和服务器才真正产生了链接,才真正可以通信了
//3.创建输入流
InputStream is=s.getInputStream();
ObjectInputStream ois=new ObjectInputStream(is);
User user = (User) (ois.readObject());
//对对象进行验证
boolean flag=false;
if(user.getId().equals("sky")&&user.getPwd().equals("123123")){
flag=true;
}
//向客户端输出结果----》操作流----《输出流
OutputStream os=s.getOutputStream();
DataOutputStream dos=new DataOutputStream(os);
dos.writeBoolean(flag);
//5.关闭流+关闭网络资源
dos.close();
os.close();
ois.close();
is.close();
s.close();
ss.close();
}
}
功能分解4:加入完整、正确的异常处理方式
public class TestClient {
public static void main(String[] args) {
Socket s=null;
OutputStream os=null;
ObjectOutputStream oos=null;
InputStream is=null;
DataInputStream dis=null;
try {
//1.创建套接字:指定服务器的IP和端口号
s= new Socket("10.170.46.230",8888);
//录入用户的账号和密码
Scanner sc=new Scanner(System.in);
System.out.println("请录入您的账号:");
String id=sc.next();
System.out.println("请录入您的密码:");
String pwd=sc.next();
User user=new User(id,pwd);
//2.对于程序员来说,向外发送数据 感受---》利用输出流
os = s.getOutputStream();
oos=new ObjectOutputStream(os);
oos.writeObject(user);
//接收服务器端的回话-----》操作流----》操作输入流
is=s.getInputStream();
dis=new DataInputStream(is);
boolean b = dis.readBoolean();
if(b){
System.out.println("登录成功!!");
}else{
System.out.println("登陆失败!");
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//3.关闭流+关闭网络资源
//为了确保每个流都能关上,需要单独加try-catch
try {
if(dis!=null){//为了防止空指针异常
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(oos!=null){
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os!=null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(s!=null){
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务器端:
public class TestServer {
public static void main(String[] args) {
//1.创建套接字:指定服务器的端口号
ServerSocket ss= null;
Socket s = null;
InputStream is=null;
ObjectInputStream ois=null;
OutputStream os=null;
DataOutputStream dos=null;
try {
ss = new ServerSocket(8888);
//2.等着客户端发来的信息
s = ss.accept();
//3.创建输入流
is=s.getInputStream();
ois=new ObjectInputStream(is);
User user = (User) (ois.readObject());
//对对象进行验证
boolean flag=false;
if(user.getId().equals("sky")&&user.getPwd().equals("123123")){
flag=true;
}
//向客户端输出结果----》操作流----《输出流
os=s.getOutputStream();
dos=new DataOutputStream(os);
dos.writeBoolean(flag);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(dos!=null){//为了防止空指针异常
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os!=null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ois!=null){
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(s!=null){
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ss!=null){
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
功能分解5:多线程接收用户请求
线程:
public class ServerThread extends Thread {//线程:专门处理客户端的请求
Socket s = null;
InputStream is=null;
ObjectInputStream ois=null;
OutputStream os=null;
DataOutputStream dos=null;
public ServerThread(Socket s) {
this.s = s;
}
@Override
public void run() {
try{
is=s.getInputStream();
ois=new ObjectInputStream(is);
User user = (User) (ois.readObject());
//对对象进行验证
boolean flag=false;
if(user.getId().equals("sky")&&user.getPwd().equals("123123")){
flag=true;
}
//向客户端输出结果----》操作流----《输出流
os=s.getOutputStream();
dos=new DataOutputStream(os);
dos.writeBoolean(flag);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(dos!=null){//为了防止空指针异常
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os!=null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ois!=null){
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务器:
public class TestServer {
public static void main(String[] args) {
System.out.println("服务器启动了!");
ServerSocket ss= null;
Socket s = null;
int count=0;//定义一个计数器,用来计数客户端的请求
try {
ss = new ServerSocket(8888);
while(true){//加入死循环,服务器一直监听客户端是否发送数据
s = ss.accept();
//每次过来的请求靠线程处理
new ServerThread(s).start();
count++;
System.out.println("当前是第"+count+"个用户访问,对应的用户是:"+s.getInetAddress().getHostName());
}
} catch (IOException e) {
e.printStackTrace();
}
//服务器启动了!
//当前是第1个用户访问,对应的用户是:DESKTOP-MUG7H60
//当前是第2个用户访问,对应的用户是:DESKTOP-MUG7H60
}
}
客户端和用户与功能分解3相同;
基于UDP的网络编程
TCP:客户端与服务器地位不平等
UDP:发送方与接收方的地位是平等的
UDP案例:完成网站的咨询聊天
功能分解1:单向通信
发送方:
public class TestSender {//发送方
public static void main(String[] args) throws IOException {
System.out.println("学生上线");
InetAddress ia=InetAddress.getByName("10.170.46.230");
//1.准备套接字:指定发送方端口号
DatagramSocket ds=new DatagramSocket(8888);
//2.准备数据包:
String str="你好";
byte[] bytes=str.getBytes();
/*
* 需要四个参数
* 1、指的是传送数据转为byte数组
* 2、byte数组的长度
* 3、封装接收方的IP
* 4、指定接收方的端口号
* */
DatagramPacket dp=new DatagramPacket(bytes,bytes.length,ia,9999);
//发送:
ds.send(dp);
//关闭资源
ds.close();
}
}
接收方:
public class TestReceiver {//接收方
public static void main(String[] args) throws IOException {
System.out.println("老师上线了");
//1.创建套接字:指定接收方的端口
DatagramSocket ds=new DatagramSocket(9999);
//2.创建空数据包
//有一个空的数据包,打算用来接收对方发送的数据包
byte[] b=new byte[1024];
DatagramPacket dp=new DatagramPacket(b,b.length);
//3.接收数据包,然后放入dp数据包接收
ds.receive(dp);//接收完以后dp里面就填充好内容了
//4.取出数据
byte[] data = dp.getData();
String s=new String(data,0,dp.getLength());//dp.getLength()数据包中的有效长度
System.out.println("学生说:"+s);
//5.关闭资源
ds.close();
}
}
功能分解2:双向通信
接收方:
public class TestReceiver {//接收方
public static void main(String[] args) throws IOException {
System.out.println("老师上线了");
//1.创建套接字:指定接收方的端口
DatagramSocket ds=new DatagramSocket(9999);
//2.创建空数据包
//有一个空的数据包,打算用来接收对方发送的数据包
byte[] b=new byte[1024];
DatagramPacket dp=new DatagramPacket(b,b.length);
//3.接收数据包,然后放入dp数据包接收
ds.receive(dp);//接收完以后dp里面就填充好内容了
//4.取出数据
byte[] data = dp.getData();
String s=new String(data,0,dp.getLength());//dp.getLength()数据包中的有效长度
System.out.println("学生说:"+s);
//6.老师进行回复
Scanner sc=new Scanner(System.in);
System.out.print("老师可以回复了:");
String str=sc.next();
byte[] bytes=str.getBytes();
//封装数据,并且指定学生的ip和端口号
DatagramPacket dps=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("10.170.46.230"),8888);
ds.send(dps);
//5.关闭资源
ds.close();
}
}
发送方:
public class TestSender {//发送方
public static void main(String[] args) throws IOException {
System.out.println("学生上线");
InetAddress ia=InetAddress.getByName("10.170.46.230");
//1.准备套接字:指定发送方端口号
DatagramSocket ds=new DatagramSocket(8888);
//2.准备数据包:
System.out.print("学生可以说话了:");
Scanner sc=new Scanner(System.in);
String str=sc.next();
byte[] bytes=str.getBytes();
/*
* 需要四个参数
* 1、指的是传送数据转为byte数组
* 2、byte数组的长度
* 3、封装接收方的IP
* 4、指定接收方的端口号
* */
DatagramPacket dp=new DatagramPacket(bytes,bytes.length,ia,9999);
//4.发送:
ds.send(dp);
//6.接收老师发送的数据
byte[] b=new byte[1024];
DatagramPacket dp2=new DatagramPacket(b,b.length);//封装数组
ds.receive(dp2);
byte[] data = dp2.getData();//取出数据
String s=new String(data,0,dp2.getLength());//dp2.getLength()数据包中的有效长度
System.out.println("老师回复:"+s);
//5.关闭资源
ds.close();
}
}
功能分解3:异常处理
发送方:
public class TestSender {//发送方
public static void main(String[] args) {
System.out.println("学生上线");
//1.准备套接字:指定发送方端口号
DatagramSocket ds= null;
try {
ds = new DatagramSocket(8888);
//2.准备数据包:
System.out.print("学生可以说话了:");
Scanner sc=new Scanner(System.in);
String str=sc.next();
byte[] bytes=str.getBytes();
/*
* 需要四个参数
* 1、指的是传送数据转为byte数组
* 2、byte数组的长度
* 3、封装接收方的IP
* 4、指定接收方的端口号
* */
DatagramPacket dp=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("10.170.46.230"),9999);
//4.发送:
ds.send(dp);
//6.接收老师发送的数据
byte[] b=new byte[1024];
DatagramPacket dp2=new DatagramPacket(b,b.length);//封装数组
ds.receive(dp2);
byte[] data = dp2.getData();//取出数据
String s=new String(data,0,dp2.getLength());//dp2.getLength()数据包中的有效长度
System.out.println("老师回复:"+s);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//5.关闭资源
ds.close();
}
}
}
接收方:
public class TestReceiver {//接收方
public static void main(String[] args) {
System.out.println("老师上线了");
//1.创建套接字:指定接收方的端口
DatagramSocket ds= null;
try {
ds = new DatagramSocket(9999);
//2.创建空数据包
//有一个空的数据包,打算用来接收对方发送的数据包
byte[] b=new byte[1024];
DatagramPacket dp=new DatagramPacket(b,b.length);
//3.接收数据包,然后放入dp数据包接收
ds.receive(dp);//接收完以后dp里面就填充好内容了
//4.取出数据
byte[] data = dp.getData();
String s=new String(data,0,dp.getLength());//dp.getLength()数据包中的有效长度
System.out.println("学生说:"+s);
//6.老师进行回复
Scanner sc=new Scanner(System.in);
System.out.print("老师可以回复了:");
String str=sc.next();
byte[] bytes=str.getBytes();
//封装数据,并且指定学生的ip和端口号
DatagramPacket dps=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("10.170.46.230"),8888);
ds.send(dps);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//5.关闭资源
ds.close();
}
}
}
功能分解4:完整正常通信
发送方:
public class TestSender {//发送方
public static void main(String[] args) {
System.out.println("学生上线");
//1.准备套接字:指定发送方端口号
DatagramSocket ds= null;
try {
ds = new DatagramSocket(8888);
//2.准备数据包:
while (true){
System.out.print("学生可以说话了:");
Scanner sc=new Scanner(System.in);
String str=sc.next();
byte[] bytes=str.getBytes();
/*
* 需要四个参数
* 1、指的是传送数据转为byte数组
* 2、byte数组的长度
* 3、封装接收方的IP
* 4、指定接收方的端口号
* */
DatagramPacket dp=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("10.170.46.230"),9999);
//4.发送:
ds.send(dp);
if(str.equals("byebye")){
System.out.println("学生下线");
break;
}
//6.接收老师发送的数据
byte[] b=new byte[1024];
DatagramPacket dp2=new DatagramPacket(b,b.length);//封装数组
ds.receive(dp2);
byte[] data = dp2.getData();//取出数据
String s=new String(data,0,dp2.getLength());//dp2.getLength()数据包中的有效长度
System.out.println("老师回复:"+s);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//5.关闭资源
ds.close();
}
}
}
接收方:
public class TestReceiver {//接收方
public static void main(String[] args) {
System.out.println("老师上线了");
//1.创建套接字:指定接收方的端口
DatagramSocket ds= null;
try {
ds = new DatagramSocket(9999);
//2.创建空数据包
//有一个空的数据包,打算用来接收对方发送的数据包
while(true){
byte[] b=new byte[1024];
DatagramPacket dp=new DatagramPacket(b,b.length);
//3.接收数据包,然后放入dp数据包接收
ds.receive(dp);//接收完以后dp里面就填充好内容了
//4.取出数据
byte[] data = dp.getData();
String s=new String(data,0,dp.getLength());//dp.getLength()数据包中的有效长度
System.out.println("学生说:"+s);
if(s.equals("byebye")){
System.out.println("学生下线了,老师也可以下线。");
break;
}
//6.老师进行回复
Scanner sc=new Scanner(System.in);
System.out.print("老师可以回复了:");
String str=sc.next();
byte[] bytes=str.getBytes();
//封装数据,并且指定学生的ip和端口号
DatagramPacket dps=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("10.170.46.230"),8888);
ds.send(dps);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//5.关闭资源
ds.close();
}
}
}