网络编程
网络编程: 网络编程主要用于解决计算机与计算机(手机、平板..)之间的数据传输问题。
- 网络编程: 不需要基于html页面就可以达到数据之间的传输。 比如: QQ , 微信等
- 网页编程: 就是要基于html页面的基础上进行数据的交互的。 比如: 珍爱网、 oa(办公自动化)、 高考的报考系统等
计算机网络: 分布在不同地域的计算机通过外部设备链接起来实现了资源共享与数据传输的计算机系统。
网络通讯的三要素:
- IP:每台计算机的唯一标识
- 端口号:用于标明该消息时给哪个对应程序处理的,只是一个标识符。
- 协议.
一、IP
1.概念
Internet上的每台主机(Host)都有一个唯一的IP地址。IP地址的长度为32位,分为4段,每段8位,用十进制数字表示,每段数字范围为0~255,段与段之间用句点隔开。
IP地址的本质是由32位二进制数据组成的:
00000000-00000000-00000000-00000000
后来人们为了方便记忆,就把IP地址划分为了4份,每份8位。例如10.124.98.214
IP地址=网络号+主机号
2.分类
IP地址是由网络号和主机号组成:
- A类地址:8位网络位,24位主机位 2^24(政府单位使用)
- B类地址:16位网络位,16位主机位 2^16(学校、银行等)
- C类地址:24位网络位,8位主机位 2^8(个人使用)
特殊的IP地址:127.0.0.1 表示本机回环地址
二、端口
1.Port地址
如果把IP地址比作一间房子 ,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口可以有65536(即:216)个。端口是通过端口号来标记的,端口号只有整数,范围是从0到65535(216-1)。
2.端口分类
- 公认端口(WellKnownPorts):从0到1023,它们紧密绑定(binding)于一些服务。
- 注册端口(RegisteredPorts):从1024到49151。它们松散地绑定于一些服务。
- 动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535。
常用端口:
FTP:21
HTTP:80
HTTPS:443
三、协议
1.TCP
TCP:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议。
特点:
- 面向连接的协议,数据传输必须要建立连接,所以在TCP中需要连接时间。
- 传输数据大小限制,一旦连接建立,双方可以按统一的格式传输大的数据。
- 一个可靠的协议,确保接收方完全正确地获取发送方所发送的全部数据。
2.UDP
UDP: User Datagram Protocol的简称, 中文名是用户数据包协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
特点:
- 每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。
- UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
- UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。
四、Java网络编程
Java网络编程是实现局域网或互联网之间的数据通信(数据传输)。
网络编程要素:
- IP(主机名)、端口、协议
- Java是OOP语言,所以将其封装为特定的对象,java.net.*
对象映射:
IP 主机名 (InetAddress)
端口 数字标识 不用封装为对象
1.InetAddress(IP类)
此类表示互联网协议 (IP) 地址。
常用方法:
getLocalHost() 获取本机的IP地址
getHostAddress() 返回一个IP地址的字符串表示形式。
getHostName() 返回计算机的主机名。
getByName(String host) 根据一个IP地址的字符串形式或者是一个主机名生成一个IP地址对象。 (用于获取别人的IP地址对象)
getAllByName(String host) 返回IP地址的数组形式
注意:
- 使用getByName(String host)通过一个IP地址找主机时,如果IP和Address没有对应在公网上,那么该IP解析不了对应的地址,而且显示为对应IP且查询很慢。
- 可以getByName(String host)找出域名对应的IP,getAllByName()可以获取多个地址。
public class Demo1 {
public static void main(String[] args) throws UnknownHostException {
//getLocalHost 获取本机的IP地址对象
/*InetAddress address = InetAddress.getLocalHost();
System.out.println("IP地址:"+address.getHostAddress());
System.out.println("主机名:"+address.getHostName());*/
//获取其他机器的IP地址对象。
//可以根据一个IP地址的字符串形式或者是一个主机名生成一个IP地址对象。
InetAddress address = InetAddress.getByName("DESKTOP-R2TB");
System.out.println("IP地址:"+address.getHostAddress());
System.out.println("主机名:"+address.getHostName());
InetAddress[] arr = InetAddress.getAllByName("www.baidu.com");//域名
}
}
端口号没有类来描述。
2.UDP和TCP协议
UDP(速度优先)
- 将数据极其源和目的封装为数据包,不需要建立连接。
- 每个数据包大小限制在64K中
- 因为无连接,所以不可靠(丢失数据包)
- 因为不需要建立连接,所以速度快
- UDP通讯不分服务端与客户端,只分发送端与接收端。
如对讲机、游戏等
TCP(数据完整性优先)
- 面向连接,有特有的通道(基于IO流)
- 在连接中传输大数据量(没有大小限制)
- 通过三次握手机制连接,可靠协议
- 通信前必须建立连接,效率稍低
- TCP是区分客户端与服务端的
如打电话、文件传输等
五.Socket编程(网络编程)
Socket
socket的英文原义是“孔”或“插座”。作为4BDS UNIX的进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄(引用)。
每个插座就是一个应用程序。
注意:
- 不同的通信规则需要定义不同的插座。
- UDP:DatagramSocket 、 DatagramPacket
- TCP:ServerSocket 、Socket
在java中网络通讯也称作为Socket(插座)通讯,要求通讯的两台器都必须要安装Socket。
不同的协议就有不同的插座(Socket)
1.UDP协议下的socket:
①DatagramSocket(udp插座服务)
UDP通讯是不分服务端与客户端的,只分发送端与接收端
②DatagramPacket(数据包类)
- DatagramSocket与DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用Socket的发送接收方法
- 关闭Socket
DatagramPacket(buf, length, address, port):
buf: 发送的数据内容
length : 发送数据内容的大小。
address : 发送的目的IP地址对象
port : 端口号。
发送端的书写步骤:
- 建立udp的服务。
- 准备数据,把数据封装到数据包中发送。发送端的数据包要带上ip地址与端口号。
- 调用udp的服务,发送数据。
- 关闭资源。
接收端的书写步骤:
- 建立udp的服务
- 准备空的数据包接收数据。
- 调用udp的服务接收数据。
- 关闭资源
代码示例:
发送端:
//发送端
public class Sender {
public static void main(String[] args) throws IOException {
//建立udp的服务
DatagramSocket datagramSocket = new DatagramSocket();
//准备数据,把数据封装到数据包中。
String data = "这个是我的第一个udp例子.";
//创建了一个数据包
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length,InetAddress.getLocalHost() , 9090);
//调用udp的服务发送数据包
datagramSocket.send(packet);
//关闭资源 (实际上就是释放占用的端口号)
datagramSocket.close();
}
}
接收端:
public class Receiver {
public static void main(String[] args) throws IOException {
//建立udp的服务 ,并且要监听一个端口。
DatagramSocket socket = new DatagramSocket(9090);
//准备空的数据包用于存放数据。
byte[] buf = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length); // 1024
//调用udp的服务接收数据
socket.receive(datagramPacket); //receive是一个阻塞型的方法,没有接收到数据包之前会一直等待。 数据实际上就是存储到了byte字节数组中了。
System.out.println("接收端接收到的数据:"+ new String(buf,0,datagramPacket.getLength())); // getLength() 获取数据包存储的字节数。
//关闭资源
socket.close();
}
}
注意:
- 发送端与接收端是两个独立的运行程序
- 发送端发送数据的端口是随机的
- IDE中的控制台切换
每个网络程序都有自己所处理的特定格式数据,如果接收到的数据不符合指定的格式,那么就会被当成垃圾数据丢弃。
假设QQ接受的数据格式如下:
version:time:sender:ip:flag:content;
- version:版本号
- time:发送时间
- sender:发送人
- ip:IP
- flag:发送标识(比如语音消息:30;文件:31;普通文字:32)
- content:真正要发送的内容
模拟使用UDP协议给QQ发送消息:
public class QQDemo {
public static void main(String[] args) throws IOException {
// 建立udp的服务
DatagramSocket socket = new DatagramSocket();
// 准备数据,把数据封装到数据包中
String data = getData("QQ你好!");
DatagramPacket packet = new DatagramPacket(data.getBytes(),
data.getBytes().length,
InetAddress.getByName("192.168.10.13"), 2425);
// 发送数据
socket.send(packet);
// 关闭资源
socket.close();
}
// 把数据拼接成指定格式的数据
public static String getData(String content) {
StringBuilder sb = new StringBuilder();
sb.append("1.0:");
sb.append(System.currentTimeMillis() + ":");
sb.append("hcx:");
sb.append("192.168.10.1:");
sb.append("32:");
sb.append(content);
return sb.toString();
}
}
在udp协议中,有一个IP地址称作为广播地址,广播地址就是主机号为255地址。给广播IP地址发送消息的时候,在同一个网络段的机器都可以接收 到信息。
实现群聊:要可以同时发和收,使用多线程
ChatSender:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
//群聊发送端
public class ChatSender extends Thread {
@Override
public void run() {
try {
//建立udp的服务
DatagramSocket socket = new DatagramSocket();
//准备数据,把数据封装到数据包中发送
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
DatagramPacket packet = null;
while((line = keyReader.readLine())!=null){
//把数据封装 到数据数据包中,然后发送数据。
packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("192.168.10.255"), 9090);
//把数据发送出去
socket.send(packet);
}
//关闭 资源
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ChatReceive:
//群聊接收端
public class ChatReceive extends Thread {
@Override
public void run() {
try {
//建立udp的服务,要监听一个端口
DatagramSocket socket = new DatagramSocket(9090);
//准备空的数据包存储数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
boolean flag = true;
while(flag){
socket.receive(packet);
// packet.getAddress() 获取对方数据包的IP地址对象。
System.out.println(packet.getAddress().getHostAddress()+":"+new String(buf,0,packet.getLength()));
}
//关闭资源
socket.close();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ChatMain:
public class ChatMain {
public static void main(String[] args) {
ChatReceive chatReceive = new ChatReceive();
chatReceive.start();
ChatSender chatSender = new ChatSender();
chatSender.start();
}
}
UDP是一个不可靠(数据包可能会丢失)的协议
以下两种情况会出现数据包丢失:
1.带宽不足 。
2.cpu的处理能力不足。
发送端:
public class Sender {
public static void main(String[] args) throws Exception {
//建立udp的服务
DatagramSocket socket = new DatagramSocket();
//准备数据,数据封装到数据中发送
DatagramPacket packet = null;
for(int i = 0 ; i< 10; i++){ //连续发送10个数据包
String data =i +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
packet = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getLocalHost(), 9090);
//发送数据包
socket.send(packet);
}
//关闭资源
socket.close();
}
}
接收端:
public class Receive {
public static void main(String[] args) throws IOException, Exception {
//建立udp的服务
DatagramSocket socket = new DatagramSocket(9090);
//建立空的数据包存储数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//不断接收数据包
while(true){
socket.receive(packet);
System.out.println(new String(buf,0,packet.getLength()));
Thread.sleep(10);
}
}
}
2.TCP协议下的socket:
①Socket(客户端)
tcp的客户端一旦启动马上要与服务端进行连接。
tcp的客户端使用步骤:
- 建立tcp的客户端服务。
- 获取到对应的流对象。
- 写出或读取数据
- 关闭资源。
②ServerSocket(服务端)
ServerSocket的使用步骤:
- 建立tcp服务端的服务。
- 接受客户端的连接产生一个Socket.
- 获取对应的流对象读取或者写出数据。
- 关闭资源。
Clinet:
public class Clinet {
public static void main(String[] args) throws IOException{
//建立tcp的服务
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//获取到Socket的输出流对象
OutputStream outputStream = socket.getOutputStream();
//利用输出流对象把数据写出即可。
outputStream.write("服务端你好".getBytes());
//获取到输入流对象,读取服务端回送的数据。
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println("客户端接收到的数据:"+ new String(buf,0,length));
//关闭资源
socket.close();
}
}
Server:
public class Server {
public static void main(String[] args) throws Exception {
//建立Tcp的服务端,并且监听一个端口。
ServerSocket serverSocket = new ServerSocket(9090);
//接受客户端的连接
Socket socket = serverSocket.accept(); //accept() 接受客户端的连接 该方法也是一个阻塞型的方法,没有客户端与其连接时,会一直等待下去。
//获取输入流对象,读取客户端发送的内容。
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = 0;
length = inputStream.read(buf);
System.out.println("服务端接收:"+ new String(buf,0,length));
//获取socket输出流对象,想客户端发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("客户端你好啊!".getBytes());
//关闭资源
serverSocket.close();
}
}
ServerSocket不设计getInputStream与getOutputStream的原因:
ServerSocket没有提供流的原因.png实现客户端实现实时的通讯:
ChatClient:
public class ChatClient {
public static void main(String[] args) throws IOException {
//建立tcp的客户端服务
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//获取socket的输出流对象。
OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream());
//获取socket的输入流对象
BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取键盘的输入流对象,读取数据
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
//不断的读取键盘录入的数据,然后把数据写出
while((line = keyReader.readLine())!=null){
socketOut.write(line+"\r\n");
//刷新
socketOut.flush();
//读取服务端回送的数据
line = socketReader.readLine();
System.out.println("服务端回送的数据是:"+line);
}
//关闭资源
socket.close();
}
}
ChatServer:
public class ChatServer {
public static void main(String[] args) throws IOException {
//建立tcp的服务端
ServerSocket serverSocket = new ServerSocket(9090);
//接受客户端的连接,产生一个SOcket
Socket socket = serverSocket.accept();
//获取到Socket的输入流对象
BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取到Socket输出流对象
OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream());
//获取键盘的输入流对象
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
//读取客户端的数据
String line = null;
while((line = socketReader.readLine())!=null){
System.out.println("服务端接收到的数据:"+ line);
System.out.println("请输入回送给客户端的数据:");
line = keyReader.readLine();
socketOut.write(line+"\r\n");
socketOut.flush();
}
//关闭资源
serverSocket.close();
}
}
注意:
1.如果使用BuffrerdReader的readline方法一定要加上\r\n才能把数据写出。
2.在达不到自动写出的情况下,使用字符流一定要调用flush方法数据才会写出。
一个服务端可以链接多个客户端:
//模拟服务器
public class ServerDemo extends Thread {
Socket socket;
public TomcatDemo(Socket socket){
this.socket = socket;
}
public void run() {
try {
//获取socket的输出流对象
OutputStream outputStream = socket.getOutputStream();
//把数据写到浏览器上
outputStream.write("<html><head><title>aaa</title></head><body>Hello world</body></html>".getBytes());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
//建立tcp的服务端
ServerSocket serverSocket = new ServerSocket(9090);
//不断的接受客户端的连接
while(true){
Socket socket = serverSocket.accept();
new TomcatDemo(socket).start();
}
}
}
实现一个服务端可以给多个客户端发送图片,并统计一共有多少个用户下载了图片。
服务端ImageServer:
public class ImageServer extends Thread {
Socket socket ;
//该集合用于存储ip地址。
static HashSet<String> ips = new HashSet<String>();
public ImageServer(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//获取到socket输出流对象
OutputStream outputStream = socket.getOutputStream();
//获取图片的输入流对象
FileInputStream fileInputStream = new FileInputStream("F:\\3.jpg");
//读取图片数据,把数据写出
byte[] buf = new byte[1024];
int length = 0 ;
while((length = fileInputStream.read(buf))!=-1){
outputStream.write(buf,0,length);
}
String ip = socket.getInetAddress().getHostAddress(); // socket.getInetAddress() 获取对方的IP地址
if(ips.add(ip)){
System.out.println("恭喜你"+ip+"下载成功,当前下载的人数是:"+ ips.size());
}
//关闭资源
fileInputStream.close();
socket.close();
}catch (IOException e) {
}
}
public static void main(String[] args) throws IOException {
//建立tcp的服务 ,并且要监听一个端口
ServerSocket serverSocket = new ServerSocket(9090);
while(true){
//接受用户的链接。
Socket socket = serverSocket.accept();
new ImageServer(socket).start();
}
}
}
客户端ImageClient:
//下载图片的客户端
public class ImageClient {
public static void main(String[] args) throws Exception{
//建立tcp的服务
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//获取socket的输入流对象
InputStream inputStream = socket.getInputStream();
//获取文件的输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("D:\\3.jpg");
//边读边写
byte[] buf = new byte[1024];
int length = 0 ;
while((length = inputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//关闭资源
fileOutputStream.close();
socket.close();
}
}
网友评论