——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
2015.10.11
2016.3.12
目录:
1. IP
2. Socket
1. IP
连接到互联网上每台计算机或其它设备都规定一个唯一的地址,有了它我们才能从千万台计算机中找到目标计算机,并与之通信。可以理解成电话号码,有了这个号码就可以和对方进行通话。
IP是由4个字节组成,也就是4组8位二进制数组成,例如01111111 00000001 00000000 00000000。通常,为了方便记忆,我们转换成十进制来表示。例如以上例子可以写成:127.1.0.0。这种表示形式就称为“点分十进制表示法”。
Java中使用InetAddress类表示IP地址。
使用InetAddress类的静态方法创建一个IP地址对象:
//使用getByName静态方法通过主机名获取IP地址(主机名为域名)
InetAddress baidu = InetAddress.getByName("www.baidu.com");
//使用getByName静态方法通过主机名获取IP地址(主机名为IP地址的字符串表示形式)
InetAddress.getByName("195.158.158.123");
// 使用InetAddress的静态getLocalHost方法获取本地IP地址
InetAddress ip = InetAddress.getLocalHost();
// 使用getHostName方法获取IP地址的主机名
String hostName = ip.getHostName();//DESKTOP-TAU56EN
// 使用getHostAddress方法获取IP地址字符串
String hostAddress = ip.getHostAddress();//192.168.1.7
// 使用isReachable方法判断该IP地址是否可到达
boolean flag = baidu.isReachable(50);// true
2. Socket
简单说套接字就是连接两个网络程序通讯的端点。IP地址标识了互联网上的主机,端口号标识了计算机上运行的进程。IP和端口号就可以组成套接字Socket对象。计算机上的端口号是一个16位的整数,范围在0-65535。其中0-1023端口号一般用于系统服务。所以为了避免冲突,一般选用1023以后的端口使用。两个程序的通信其实就是两个Socket对象间的通信,例如你要去存钱,首先得去找银行进入银行营业大厅,进去之后,你要办理什么类型的服务就去找对应的窗口,比如存取钱在一个窗口,办理外汇在另一个窗口。这就是Socket对象包含IP地址和端口的作用。
套接字是一种模式,要建立连接还要有协议。开放系统互联模型(OSI)中,传输层为会话层提供了端到端的数据传输服务,使用的协议有UDP、TCP。它们是比较基本的协议,其它协议例如FTP、HTTP、DNS等是 基于UDP、TCP建立的。
创建基于UDP的连接
首先来创建发送端部分:
// 创建用来发送数据报包的套接字
DatagramSocket client = new DatagramSocket();
// 创建数据报包
byte[] bys = "这里是客户端".getBytes();
DatagramPacket data = new DatagramPacket(bys, bys.length,
InetAddress.getLocalHost(), 58888);
//发送数据报包
client.send(data);
// 关闭套接字
client.close();
接收端部分:
// 创建接收端数据报包套接字
DatagramSocket ser = new DatagramSocket(58888);
// 创建数据报包
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
// 接收数据报包
ser.receive(packet);
// 解析数据报包
int len = packet.getLength();
data = packet.getData();
System.out.println(new String(data, 0, len));
// 关闭套接字
ser.close();
由以上例子中我们来总结创建一个UDP连接的基本步骤:
A:创建发送端/接收端套接字
B:创建数据报包
C:发送/接收解析数据报包
D:关闭Socket
UDP协议的特点:
UDP是面向无线连接的协议,发送端和接收端只负责数据报包的发送和接收,数据报包内部包含传送地址,数据发送后可能会通过不同的路由到达地址。
UDP协议不能对数据的发送进行任何的保证,适合传输可靠性要求不高的情况比如QQ、短信。
API中的方法:
构造方法:
创建空的套接字
——–DatagramSocket()
创建数据报套接字,将其绑定到指定的本地地址
——–DatagramSocket(int port, InetAddress laddr)
发送接收方法:
从此套接字接收数据报包
——–void receive(DatagramPacket p)
从此套接字发送数据报包
——– void send(DatagramPacket p)
创建基于TCP的连接
发送端部分:
// 创建用来发送数据报包的套接字
Socket client = new Socket(InetAddress.getLocalHost(), 58888);
// 获取TCP通道输出流
PrintWriter pw = new PrintWriter(client.getOutputStream(), true);
pw.println("这里是客户端");
// 关闭套接字
client.close();
接收端部分:
// 创建接收端数据报包套接字
ServerSocket ser=new ServerSocket(58888);
// 监听Socket
Socket get=ser.accept();
// 获取TCP通道输入流
BufferedReader br=new BufferedReader(new InputStreamReader(get.getInputStream()));
// 读取数据
String line=br.readLine();
System.out.println(line);
// 关闭套接字
ser.close();
由以上例子中我们来总结创建一个TCP连接的基本步骤:
A:创建发送/接收端套接字
B:建立连接
C:获取输出/输入流
D:传输数据
E:关闭套接字
API中的方法:
发送端套接字的方法:
创建一个流套接字并将其连接到指定 IP 地址的指定端口号
——–Socket(InetAddress address, int port)
创建一个流套接字并将其连接到指定主机上的指定端口号。
——–Socket(String host, int port)
返回此套接字的输入流。
——–InputStream getInputStream()
返回此套接字的输出流
——–OutputStream getOutputStream()
此套接字的输入流置于“流的末尾”
——–void shutdownInput()
禁用此套接字的输出流。
——–void shutdownOutput()
接收端套接字的方法:
创建绑定到特定端口的服务器套接字
——–ServerSocket(int port)
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号
——–ServerSocket(int port, int backlog)
将 ServerSocket 绑定到特定地址(IP 地址和端口号)
——–void bind(SocketAddress endpoint)
侦听并接受到此套接字的连接,阻塞式
——–Socket accept()
通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位
——–void setSoTimeout(int timeout)
TCP协议的特点:
TCP是面向连接的协议,发送端和接收端必须建立连接通道后才能进行数据的传送,因此TCP的建立开销大,同时由于建立了通道,所以数据的传送量大;TCP协议建立了完善的机制来确保数据可靠的传送。因此TCP协议适合电子邮件、HTTP等。
示例:
利用上面学到的知识,制作一个多客户端上传文件到同一个服务器端的简单程序。本例中使用了TCP协议来传送数据。
服务端程序:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/*
*
*服务器接收端,可以同时接收多个客户端的文件的上传,并按照上传文件的文件名保存到指定的文件夹下。
*
* */
public class Server {
// 服务器TCP协议的套接字
final ServerSocket server;
// 接收客户端文件的文件夹
final File receiveFolder;
public Server() throws IOException {
// 创建一个端口为58888服务器套接字
this.server = new ServerSocket(58888);
// 初始化receiveFolder,客户端上传的文件将保存在该文件夹内
receiveFolder = new File("D:\\receiveFolder");
// 监测该文件夹是否被创建,否则新建
if (!receiveFolder.exists())
receiveFolder.mkdirs();
// 监测来自客户端的请求
while (true) {
// 使用accept方法监测套接字请求,在没有请求时,该方法将一直阻塞
Socket client = server.accept();
// 启动客户端处理线程
new Thread(new ClientThread(client)).start();
}
}
// 该线程类用于处理客户端的请求
private class ClientThread implements Runnable {
// 客户端的套接字
final Socket client;
// 客户端的地址
final InetAddress clientIP;
// TCP通道输入流
final BufferedInputStream bis;
// TCP通道输出流
final BufferedOutputStream bos;
// final String FILENAME = "*._.*";
ClientThread(final Socket client) throws IOException {
this.client = client;
// 初始化客户端的IP地址
this.clientIP = client.getInetAddress();
// 使用Socket.getInputStream()获取通道输入流
this.bis = new BufferedInputStream(client.getInputStream());
// 使用Socket.getOutputStream()获取输出流
this.bos = new BufferedOutputStream(client.getOutputStream());
}
@Override
public void run() {
// 上传的文件
File receive = null;
byte[] bys = new byte[1024];
int length = 0;
// 接收文件名,并创建receive
try {
// 接收文件名
String fileName = null;
// 读取通道输入流中的数据
if ((length = bis.read(bys)) != -1) {
fileName = new String(bys, 0, length);
}
// 发送反馈
String sign = "文件名获取成功";
bos.write(sign.getBytes());
bos.flush();
// 获取客户端的主机名
String clientHostName = clientIP.getHostName();
System.out.println(fileName);
// 上传的文件将保存到以客户端主机名命名的文件夹下
File folder = new File(receiveFolder, clientHostName);
folder.mkdirs();
// 上传的文件
receive = new File(folder, fileName);
} catch (IOException e) {
e.printStackTrace();
}
// 接收文件,写入到硬盘
// 创建缓冲输出流,接收客户端文件数据并写入到硬盘,使用try-with-sources语句自动关闭输出流
try (BufferedOutputStream write = new BufferedOutputStream(new FileOutputStream(receive));) {
while ((length = bis.read(bys)) != -1) {
// 写出数据
write.write(bys, 0, length);
// 刷新
write.flush();
}
} catch (IOException e) {
// 处理接收文件部分的异常
System.out.println("接收文件异常");
}
// 文件接收完毕,发送反馈并关闭套接字
try {
byte[] sign = "服务器端接收完毕".getBytes();
bos.write(sign);
bos.flush();
// 关闭此客户端套接字,与此关联的通道流(bis和bos)也随之关闭
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
// 启动服务器
new Server();
}
}
客户端程序:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* TCP协议的客户端,可以上传文件到服务器
*/
public class Client {
// 客户端套接字
final Socket client = new Socket(InetAddress.getLocalHost(), 58888);
// 上传的文件
File target;
// TCP通道输入流
final BufferedInputStream bis;
// TCP通道输出流
final BufferedOutputStream bos;
public Client(final Path path) throws UnknownHostException, IOException {
// 选择上传的文件
target = path.toFile();
// 使用缓冲字节流包装通道输入流
bis = new BufferedInputStream(client.getInputStream());
// 使用缓冲字节流包装通道输出流
bos = new BufferedOutputStream(client.getOutputStream());
}
// 上传文件的方法
public void upLoad() {
byte[] bys = new byte[1024];
int len = 0;
// 上传文件名
String fileName = target.getName();
try {
// 将文件名写入通道输出流
bos.write(fileName.getBytes());
// 刷新
bos.flush();
// 读取文件名上传成功的反馈
len = bis.read(bys);
String sign = new String(bys, 0, len);
if (sign.equals("文件名获取成功"))
System.out.println("准备上传文件,请稍等...");
} catch (IOException i) {
System.out.println("上传文件名异常");
}
// 上传文件
try (BufferedInputStream read = new BufferedInputStream(new FileInputStream(target))) {
// 读取文件数据
while ((len = read.read(bys)) != -1) {
// 将数据写入通道输出流
bos.write(bys, 0, len);
// 刷新
bos.flush();
}
// 关闭客户端套接字的输出流
client.shutdownOutput();
} catch (IOException i) {
System.out.println("上传文件异常");
}
// 读取服务器的反馈
try {
len = bis.read(bys);
String sign = new String(bys, 0, len);
// 打印反馈到控制台
System.out.println(sign);
// 关闭套接字
client.close();
} catch (IOException i) {
System.out.println("接收反馈异常");
}
}
public static void main(String[] args) throws IOException, IOException {
// 测试
Client test = new Client(Paths.get("D:\\迅雷游戏\\XLGameBox\\Uninstaller.exe"));
test.upLoad();
}
}