软件结构
C/S结构 :
全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
B/S结构 :
全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机
的通信的程序。
网络编程
- 网络编程就是Socket编程,Socket编程就是网络编程。
网络编程的三要素:
- IP地址
- 端口号
- 协议
IP地址的作用:
- 网络设备的唯一标识。
- 通过IP地址可以找到对应的计算机。
IP的常用命令:
- 查看本机IP地址,在控制台(cmd)输入:ipconfig
- 检查网络是否连通,在控制台输入:ping 空格 IP地址
- 特殊的IP地址:本机IP地址: 127.0.0.1 、localhost 。
IP分类:IPV4和IPV8
IPV4格式:
- xxx.xxx.xxx.xxx 由四段数字 组成,每段数字的取值范围是0到255,每一个IP地址有4个字节组成。
IPV6格式:
每一个IP地址由16个字节组成。
端口号的概述:
就是一个十进制的整数。
作用:
进程的唯一标识,每一个程序都会有一个端口号。
取值范围:
0 到 65535
注意事项:
- 0 到 1023 的端口号是系统保留使用的,程序员要使用 1024 或 1024 以上的端口号。
协议作用:
用来规定计算机与计算机之间数据传输的格式。
协议的分类:
- UDP协议
- TCP协议
小结:
- 通过IP找主机,通过端口号找程序,通过协议确定如何传输数据。
InetAddress类
InetAddress类概述:
一个该类的对象就代表一个IP地址。
InetAddress类静态方法:
static InetAddress getLocalHost()
- 获得本地主机IP地址对象(获得自己的)。
- 主机名 / IP地址字符串
static InetAddress getByName(String host)
- 根据主机名或IP地址字符串获得IP地址对象 (获得别人的)。
InetAddress类成员方法:
String getHostName()
- 获得主机名。
String getHostAddress()
- 获得IP地址字符串。
UDP协议
UDP ==> User DatagramPacket Protocol ==> 用户数据报包协议
UDP协议的特点:
- 面向无连接协议。
- 只管发送,不确定对方是否收到。
- 基于数据包(报)传输数据 : 将要发送的数据,接受端IP地址,端口号等信息打包到一个数据包中发送。
- 传输数据大小限制在64k以内。
- 速度快,但不可靠(会出现数据丢失的情况)。
UDP协议的使用场景:
- 即时通讯(qq,微信,陌陌)。
- 在线视频。
- 网络语音电话
UDP协议通讯两个相关类:
DatagramPacket
- 数据包对象。
- 作用:用来封装要发送或接收的数据。
- 比喻:集装箱。
DatagramSocket
- 发送或接收对象。
- 作用:用来发送数据包或接收数据包。
- 比喻:码头。
DatagramPacket类构造方法:
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
- 创建发送端的数据包对象。
- buf:字节数组,用来封装要发送的数据。
- length:要发送内容的长度 单位是:字节。
- address:接收端的IP地址对象。
- port:接收端的端口号。
DatagramPacket(byte[] buf, int length)
- 创建接收端的数据包对象。
- buf:字节数组,用来封装接收到内容。
- length:能够接收内容的长度,单位:字节。
DatagramSocket类构造方法:
DatagramSocket()
- 创建发送端的Socket对象,随机生成一个端口号。
DatagramSocket(int port)
- 根据端口号创建Socket对象。(发送端创建不建议,接收端必须要指定端口号)
DatagramSocket类成员方法:
void send(DatagramPacket dp)
- 发送数据包。
void receive(DatagramPacket dp)
- 接受数据包。
接收端专用方法:
Inetaddress getAddress()
- 获得发送端的IP对象。
Int getPort()
- 获得发送端的端口号。
Int getlength()
- 获得实际接受到的字节个数。
UDP协议示例代码:
public class UDPSender {
public static void main(String[] args) throws IOException {
byte[] bytes = "你好".getBytes();
DatagramPacket dp = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),6666);
DatagramSocket ds = new DatagramSocket();
ds.send(dp);
ds.close();
}
}
public class UDPReceive {
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket(6666);
byte[] bf = new byte[1024];
DatagramPacket dp = new DatagramPacket(bf,bf.length);
ds.receive(dp);
int length = dp.getLength();
System.out.println(new String(bf,0,length));
System.out.println(dp.getAddress().getHostAddress());
System.out.println(dp.getPort());
ds.close();
}
}
TCP协议
TCP协议的概述:
- TCP协议是面向连接的通信协议,即在传输数据前先在客户端和服务器端建立逻辑连接,然后再传输数据。它
提供了两台计算机之间可靠无差错的数据传输。
TCP协议的特点:
- 面向连接的协议。
- 通过三次握手建立连接:形成数据传输通道。
- 通过四次挥手断开连接。
- 基于IO流进行数据传输。
- 传输数据大小没有限制。
- 数度慢但可靠。
TCP协议的使用场景:
- 文件上传下载。
- 邮件发送和接收。
- 远程登录。
TCP协议相关的两个类:
Socket
- 一个该类的对象就代表一个客户端程序。
ServerSocket
- 一个该类的对象就代表一个服务器端程序。
Socket类构造方式:
Socket(String host, int port)
- 根据服务器端IP地址和端口号创建客户端Socket对象。
- host:服务器IP地址。
- port:服务器端口号。
- 注意:一旦执行该方法,就会立即连接指定的服务器,如果服务器没有开启,则连接失败抛出异常。
Socket类成员方法:
public void close()
- 关闭Socket。
- 一旦一个socket被关闭,它不可再使用。
- 关闭此socket也将关闭相关的InputStream和OutputStream 。
- 关闭Socket的作用是:释放端口号。
OutputStream getOutputStream()
- 获得字节输出流对象
InputStream getInputStream()
- 获得字节输入流对象。
InetAddress getInetAddress()
- 服务器专用,用来获得进入服务器该客户端的对象。
void shutdownOutput()
- 表示输出数据结束。
- 注意:一但shutdownOutput(),那么就不能再输出数据,重新getOutputStream也不可以。当循环输出后面还要用输入流或输出流时,则一定要用shutdownOutput,否则不能终止传输,会报错。若不是循环输出或者后面没有使用输入流或输出流,则可以不用shutdownOutput。
ServerSocket类构造方法:
ServerSocket(int port)
- 根据端口号创建服务器端Socket对象, 相当于开启了一个服务器。
ServerSocket类成员方法:
Socket accept()
- 等待客户端连接并获得与客户端相关的Socket对象。
- 该方法是阻塞方法,会一直阻塞直到建立连接。
void close()
- 关闭ServerSocket。
TCP客户端实现步骤:
- 创建Socket对象指定服务器端IP地址和端口号。
- 调用Socket对象的getOutputStream方法获得字节输出流对象。
- 调用字节输出流对象的write方法往服务器端输出数据。
- 调用Socket对象的getInputStream方法获得字节输入流对象。
- 调用字节输入流对象的read方法读取服务器端返回的数据。
- 关闭Socket对象断开连接。
TCP服务器端代码实现步骤:
- 创建ServerSocket对象并指定端口号。
- 调用ServerSocket对象的accept方法等待客户端连接并获得与客户端相关的Socket对象。
- 调用Socket对象的getInputStream方法获得字节输入流对象。
- 调用字节输入流对象的read方法读取客户端发送的数据。
- 调用Socket对象的getOutputStream方法获取字节输出流对象。
- 调用字节输出流对象的write方法往客户端返回数据。
- 调用close方法关闭Socket和ServerSocket对象。
- 可以只关闭Socket,不关闭ServerSocket。
- 可以都关闭。
- 可以都不关闭。
- 若只关闭ServerSocket,不关闭Socket,会报错。
文件上传的案例:
图解:
示例代码:
客户端:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Sender {
public static void main(String[] args) throws IOException {
Socket s = new Socket("192.168.81.56",8888);
OutputStream os = s.getOutputStream();
FileInputStream fis = new FileInputStream("f:/a.png");
int len = -1;
byte[] bf = new byte[1024];
while ((len = fis.read(bf)) != -1){
os.write(bf,0,len);
}
s.shutdownOutput();
fis.close();
InputStream is = s.getInputStream();
len = is.read(bf);
System.out.println(new String(bf,0,len));
s.close();
}
}
服务器:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Receive {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8888);
Socket socket = ss.accept();
try(ss;socket) {
InputStream is = socket.getInputStream();
String s = "f:/" + System.currentTimeMillis() + ".png";
FileOutputStream fos = new FileOutputStream(s);
int len = -1;
byte[] bf = new byte[1024];
while((len = is.read(bf)) != -1){
fos.write(bf,0,len);
}
fos.close();
System.out.println(socket.getInetAddress());
OutputStream os = socket.getOutputStream();
os.write("上传成功".getBytes());
}catch (Exception e){
OutputStream stream = socket.getOutputStream();
stream.write("上传失败".getBytes());
}
}
}
若要实现多线程:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Receive1 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
while(true){
Socket socket = ss.accept();
new Thread(() -> {
try(socket) {
InputStream is = socket.getInputStream();
String s = "f:/" + System.currentTimeMillis() + ".png";
FileOutputStream fos = new FileOutputStream(s);
int len = -1;
byte[] bf = new byte[1024];
while((len = is.read(bf)) != -1){
fos.write(bf,0,len);
}
fos.close();
System.out.println(socket.getInetAddress());
OutputStream os = socket.getOutputStream();
os.write("上传成功".getBytes());
}catch (Exception e){
OutputStream stream = null;
try {
stream = socket.getOutputStream();
stream.write("上传失败".getBytes());
} catch (IOException e1) {
e1.printStackTrace();
}
}
}).start();
}
}
}