目录
相互连接的计算机通过网络进行数据的交换和资源共享
IP地址
IP地址介绍
计算机之间相互通信,也是需要地址的,即IP地址
IP地址的组成
IP地址(Internet Protocol):唯一标识网络上的每一台计算机
IP地址:32位,由4个8位二进制组成.IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制整数。例:点分十进IP地址(100.4.5.6),实际上是32位二进制数(01100100.00000100.00000101.00000110)。
IP地址 = 网络地址 +主机地址
网络地址:标识计算机或网络设备所在的网段
主机地址:标识特定主机或网络设备
分为A,B,C,D,E五类地址
IP地址的配置和检测
查看IP地址,检测网络是否畅通
查看本机的IP地址
ipconfig
测试网络是否通畅
ping 目标IP地址
网络通信协议
为了在网络中不同的计算机之间进行通信而建立的规则、标准或约定的集合
套接字(Socket)
网络通信使用IP地址标识Internet.上的计算机,使用端口号标识服务器上的进程(程序)。也就是说,如果服务器上的一个程序不占用-一个端口号,用户程序就无法找到它,就无法和该程序交互信息。端口号规定为一个16位的0~ -65535之间的整数,其中,0~1023 被预先定义的服务通信占用(如telnet占用端口23,http 占用端口80等),除非需要访问这些特定服务,否则,就应该使用1024 -65535这些端口中的某一个进行通信,以免发生端口冲突。
当两个程序需要通信时,它们可以通过使用Socket类建立套接字对象并连接在一起(端口号与IP地址的组合得出一个网络套接字)
通信链路的端点就被称为“套接字”(英文名Socket) 是提供给应用程序的接口
进行网络编程的类在java.net包
基于TCP协议的Socket编程
基于TCP协议的Socket网络通信
用来实现双向安全连接网络通信
Socket通信模型
进行网络通信时,Socket需要借助数据流来完成数据的传递工作
Socket网络编程一般可以分成如下步骤进行
网络编程模型:客户端/服务器(C/S)
客户端与服务器端运作过程:
客户端通过输出流将请求信息发送给服务器端
服务器端通过输入流读取客户端的请求信息
服务器端通过输出流将响应信息发送给客户端
客户端通过输入流读取服务器端的响应信息
package demo01;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) {
//创建通信链路的端点客户端套接字
Socket socket = null;
OutputStream os = null;
InputStream is =null;
BufferedReader br =null;
try {
socket = new Socket("127.0.0.1", 10088);
//获取输出流将数据发送出去
os =socket.getOutputStream();
String str = "用户名:张三,密码:123456";
byte[] bytes=str.getBytes();
//通过输出流调用方法将信息发送出去
os.write(bytes);
System.out.println("我是客户端,我将数据发送完毕");
//关闭通道
socket.shutdownOutput();
//客户端需要通过输入流读取服务器端发送过来的消息
is =socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String result =br.readLine();
System.out.println("我是客户端,接收到的服务器端响应信息为:"+result);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
br.close();
is.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package demo01;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
//创建服务器端套接字ServerSocket
ServerSocket ss = null;
Socket socket =null;
InputStream is =null;
BufferedReader br =null;
OutputStream os =null;
try {
ss = new ServerSocket(10088);
//服务器通过调用侦听方法来获取客户端的请求
socket =ss.accept();
//通过返回的Socket对象调用方法获取一个输入流来读取客户端发送过来的消息
is =socket.getInputStream();
//通过 输入流读取客户端发送的消息
br = new BufferedReader(new InputStreamReader(is));
String str =br.readLine();
System.out.println("我这边是服务器:客户端发送给我的数据是:"+str);
socket.shutdownInput();
//服务器接收客户端消息后,需要给客户端一个响应信息
os =socket.getOutputStream();
String result = "用户名和密码正确,可以登录";
byte[] bytes = result.getBytes();
os.write(bytes);
System.out.println("给客户端的响应信息发送成功");
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
os.close();
br.close();
is.close();
socket.close();
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
多线程处理多请求
实现多客户请求
采用多线程的方式
一个专门负责监听的应用主服务程序
一个专门负责处理请求的线程程序
客户端和服务端如何做
客户端跟之前客户端做的事情没有任何改变
客户端通过输出流发送请求信息
客户端通过输入流获取服务器端的响应信息
服务器端做的事情跟之前服务器端做的事情不一样
服务器端循环去侦听客户端的请求,侦听到一个请求就会获取一个Socket类对象,将这个Socket类对象作为参数传递给服务器线程类的有参构造方法里去
服务器线程类通过获取到的Socket类对象去执行原来服务器所做的事情
通过输入流获取客户端的请求信息
通过输出流发送响应信息给客户端
服务器端
package demo03;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
//创建ServerSocket类对象
ServerSocket ss =null;
Socket socket =null;
try {
ss = new ServerSocket(10088);
while(true){
socket =ss.accept();
//将获取到的socket对象传递到线程类中
ServerThread st = new ServerThread(socket);
st.start();
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
多个客户端
package demo03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client1 {
public static void main(String[] args) {
//创建通信链路的端点客户端套接字
Socket socket = null;
OutputStream os = null;
ObjectOutputStream oos =null;
InputStream is =null;
BufferedReader br =null;
try {
socket = new Socket("127.0.0.1", 10088);
//获取输出流将数据发送出去
os =socket.getOutputStream();
//准备Student类对象
Student stu = new Student("张三",19);
oos = new ObjectOutputStream(os);
oos.writeObject(stu);
System.out.println("我是客户端1,我将数据发送完毕");
//关闭通道
socket.shutdownOutput();
//客户端需要通过输入流读取服务器端发送过来的消息
is =socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String result =br.readLine();
System.out.println("我是客户端1,接收到的服务器端响应信息为:"+result);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
br.close();
is.close();
oos.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package demo03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client2 {
public static void main(String[] args) {
//创建通信链路的端点客户端套接字
Socket socket = null;
OutputStream os = null;
ObjectOutputStream oos =null;
InputStream is =null;
BufferedReader br =null;
try {
socket = new Socket("127.0.0.1", 10088);
//获取输出流将数据发送出去
os =socket.getOutputStream();
//准备Student类对象
Student stu = new Student("李四",22);
oos = new ObjectOutputStream(os);
oos.writeObject(stu);
System.out.println("我是客户端2,我将数据发送完毕");
//关闭通道
socket.shutdownOutput();
//客户端需要通过输入流读取服务器端发送过来的消息
is =socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String result =br.readLine();
System.out.println("我是客户端2,接收到的服务器端响应信息为:"+result);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
br.close();
is.close();
oos.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package demo03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client3 {
public static void main(String[] args) {
//创建通信链路的端点客户端套接字
Socket socket = null;
OutputStream os = null;
ObjectOutputStream oos =null;
InputStream is =null;
BufferedReader br =null;
try {
socket = new Socket("127.0.0.1", 10088);
//获取输出流将数据发送出去
os =socket.getOutputStream();
//准备Student类对象
Student stu = new Student("如花",16);
oos = new ObjectOutputStream(os);
oos.writeObject(stu);
System.out.println("我是客户端3,我将数据发送完毕");
//关闭通道
socket.shutdownOutput();
//客户端需要通过输入流读取服务器端发送过来的消息
is =socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String result =br.readLine();
System.out.println("我是客户端3,接收到的服务器端响应信息为:"+result);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
br.close();
is.close();
oos.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
UDP数据报
套接字是基于TCP协议的网络通信,即客户端程序和服务器端程序是有 连接的,双方的信息是通过程序中的输入、输出流来交互的,使得接收方收到信息的顺序和发送方发送信息的顺序完全相同,就像生活中双方使用电话进行信息交互一样。
本节介绍Java中基于UDP (用户数据报协议)协议的网络信息传输方式。基于UDP的通信和基于TCP的通信不同,基于UDP的信息传递更快,但不提供可靠性保证。也就是说,数据在传输时,用户无法知道数据能否正确到达目的地主机,也不能确定数据到达目的地的顺序是否和发送的顺序相同。可以把UDP通信比作生活中的邮递信件,我们不能肯定所发的信件就一定能够到达目的地,也不能肯定到达的顺序是发出时的顺序,可能因为某种原因导致后发出的先到达。既然UDP是一种不可靠的协议,为什么还要使用它呢?如果要求数据必须绝对准确地到达目的地,显然不能选择UDP协议来通信。但有时候人们需要较快速地传输信息,并能容忍小的错误,就可以考虑使用UDP协议。
基于UDP通信的基本模式是:
将数据打包(好比将信件装入信封一-样),称为数据包,然后将数据包发往目的地。
接收发来的数据包(好比接收信封一样),然后查看数据包中的内容。
基于UDP协议的Socket编程
基于UDP协议的Socket网络编程步骤
利用 DatagramPacket 对象封装数据包
利用 DatagramSocket 发送数据包
利用 DatagramSocket 接收数据包
利用 DatagramPacket 处理数据包
package demo04;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.net.SocketException;
/**
* 服务器端
* */
public class LoginServer {
public static void main(String[] args) {
byte[] infos=new byte[1024];
DatagramPacket dp=new DatagramPacket(infos, infos.length);
DatagramSocket socket=null;
try {
socket=new DatagramSocket(5000);
//接收客户端的数据包,并将信息封装在dp中
socket.receive(dp);
//构建一个字符串
String info=new String(dp.getData(),0,dp.getData().length);
System.out.println("客户端说:"+info);
//给客户端一个响应
String reply="您好,我在,请说!";
//客户端的地址
SocketAddress sa=dp.getSocketAddress();
//打一个包裹
DatagramPacket dp1=
new DatagramPacket(reply.getBytes(),0,reply.getBytes().length,sa);
//将包裹寄走
socket.send(dp1);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
socket.close();
}
}
}
package demo04;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/*
* 客户端
* */
public class LoginClient {
public static void main(String[] args) {
String info="您好,我想咨询一个问题!";
byte[] infos=info.getBytes();
DatagramSocket socket=null;
try {
InetAddress ia=InetAddress.getByName("localhost");
//构建客户端要发送的数据包对象
DatagramPacket dp=new DatagramPacket(infos, infos.length,ia,5000);
//客户端需要一个DatagramSocket对象
socket=new DatagramSocket();
//通过DatagramSocket对象发送数据包到服务器
socket.send(dp);
//接收服务器的响应
byte[] replys=new byte[1024];
DatagramPacket dp1=new DatagramPacket(replys, replys.length);
socket.receive(dp1);
String reply=new String(dp1.getData(),0,dp1.getData().length);
System.out.println("服务器回应:"+reply);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
//释放资源
socket.close();
}
}
}
TCP与UDP的主要区别:
TCP | UDP | |
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |