网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。 Java Socket(套接字)通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
TCP/IP 是指 TCP 和 IP 这两种协议,同时TCP属于传输层,IP属于网络层;通常使用的网络(包括互联网)是在 TCP/IP 协议族的基础上运作 的。而 HTTP 属于它内部的一个子集。
端口
- 用于区分不同应用程序
- 端口的号范围0-65535,其中0-1023为系统所保留
- IP地址和端口号组成了所谓的socket,socket是网络用运行的程序之间双向通信链路的终结点,是TCP和UDP的基础
Java针对网络通信的不同层次,提供四大类网络功能
- InetAddress:用于标识网络上的硬件资源
- URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据
- Sockets:使用TCP协议实现网络通信的Socket相关类
- Datagram:使用UDP协议,将数据保存到数据报文中,通过网络进行通信
InetAddress
public class InetAddressDemoOne {
public static void main(String[] args) throws UnknownHostException {
//获取本机的InetAddress对象
InetAddress address = InetAddress.getLocalHost();
System.out.println("计算机名:" + address.getHostName());
System.out.println("ip地址:" + address.getHostAddress());
//打印InetAddress对象
System.out.println("InetAddress对象:" + address);
//通过主机名获取InetAddress对象
InetAddress address1 = InetAddress.getByName("Tony");
System.out.println("计算机名:" + address1.getHostName());
System.out.println("ip地址:" + address1.getHostAddress());
System.out.println("InetAddress对象:" + address1);
}
}
代码输出结果:
计算机名:Tony
ip地址:192.168.56.1
InetAddress对象:Tony/192.168.56.1
计算机名:Tony
ip地址:192.168.56.1
InetAddress对象:Tony/192.168.56.1
URL
使用URL读取网页内容
- 通过URL对象的openStream()方法可以得到相应资源的数据输入流
- 通过输入流可以读取,访问网络上的数据
URL的代码使用演示:
public class UrlDemo {
public void testUrlOne() throws MalformedURLException{
URL url = new URL("http://www.baidu.com:8080");
URL urlOne = new URL(url, "/index.html?username=tony#one");
System.out.println("协议:" + urlOne.getProtocol());
System.out.println("主机: " + urlOne.getHost());
//若没有标明端口号,便会输出-1,现在标明端口号为8080,便输出8080
System.out.println("端口:" + urlOne.getPort());
System.out.println("文件路径:" + urlOne.getPath());
System.out.println("文件名:" + urlOne.getFile());
System.out.println("查询字符串:" + urlOne.getQuery());
System.out.println("锚点位置:" + urlOne.getRef());
}
public void testUrlTwo() {
try {
URL url = new URL("http://www.baidu.com");
//通过openStream()方法得到指定资源的输入流
InputStream in = url.openStream();
//通过输入流读取网络上的数据
InputStreamReader irs = new InputStreamReader(in, "utf-8");
BufferedReader br = new BufferedReader(irs);
String data = br.readLine();
while (data != null) {
System.out.println(data);
data = br.readLine();
}
br.close();
irs.close();
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public static void main(String[] args) throws MalformedURLException {
UrlDemo url = new UrlDemo();
//URL类的基本使用
url.testUrlOne();
//读取网页中的内容
url.testUrlTwo();
}
}
testUrlOne()方法输出的结果为:
协议:http
主机: www.baidu.com
端口:8080
文件路径:/index.html
文件名:/index.html?username=tony
锚点位置:one
查询字符串:username=tony
Socket通信
TCP协议是面向连接,可靠的,有序的,以字节流的方式发送数据的
基于TCP协议实现的网络通信的类:
- 客户端的Socket类
- 服务器端的ServerSocket类
Socket通信实现的步骤:
- 创建ServerSocket和Socket
- 打开连接到Socket的输出、输入流
- 按照协议对Socket进行读写操作
- 关闭输入输出流,关闭Socket
服务端的实现:
- 创建ServerSocket对象
- 通过accept()方法监听客户端的请求
- 连接建立后,通过输入流读取客户端的请求信息
- 通过输出流向客户端发送响应信息
- 关闭相关资源
客户端的实现:
- 创建Socket对象,指明需要连接的服务器地址
- 连接建立后,通过输出流向服务器端发送请求信息
- 通过输入流获取服务器响应的信息 4.关闭相关资源
多线程服务器实现:
- 服务器端创建ServerSocket,循环调用accept()
- 客户端创建一个socket并和服务器端连接
- 服务端接受客户端请求,创建一个socket与该客户连接
- 建立连接的两个socket在一个单独的线程上对话
- 服务端继续等待新的连接
客户端的事例代码:
public class Client {
public void createScoket() {
try {
//1.创建客户端Socket,指定服务器的地址与端口
Socket socket = new Socket("localhost", 8080);
//2.获取输出流,向服务器发送信息
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("用户名:tony; 密码: 1234");
pw.flush();
socket.shutdownOutput();
//3.获取输入流,获取服务器的响应信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while ((info = br.readLine()) != null) {
System.out.println("我是客户端,服务端说:" + info);
}
//4.关闭资源
socket.close();
} catch (UnknownHostException uhe) {
uhe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public static void main(String[] args) {
Client client = new Client();
client.createScoket();
}
}
多线程类的事例代码:
public class ServerThread implements Runnable {
//和本线程相关的Socket
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
//线程执行的操作,响应客户端的请求
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
String info ;
while ((info = br.readLine()) != null) {
System.out.println("我是服务器,客户端说:" + info);
}
socket.shutdownInput();
pw.write("欢迎您!");
pw.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务端的事例代码:
public class Server {
public void createServerSocket() throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = null;
int count = 0;
System.out.println("***服务器即将启动,等待客户端的连接***");
while (true) {
socket = serverSocket.accept();
//创建一个线程
ServerThread servereThread = new ServerThread(socket);
Thread thread = new Thread(servereThread);
//启动线程
thread.start();
count++;
System.out.println("客户端的数量为:" + count);
System.out.println("当前客户端的IP:" + socket.getInetAddress().getHostAddress());
}
}
public static void main(String[] args) throws IOException {
Server server = new Server();
server.createServerSocket();
}
}
UDP编程
进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要送达的Socket(主机地址和端口号),然后再将数据报发送出去 UDP相关的操作类:
- DatagramPacket:表示数据报包
- DatagramSocket:进行端到端通信的类
服务端(客户端)实现:
- 创建DatagramSocket,指定端口号
- 创建DatagraPacket
- 接受客户端发送的数据信息
- 读取数据
客户端实现的事例代码:
public class UDPClient {
public void createDatagramSocket() throws IOException {
/*
* 向服务器发送数据
*/
//1.定义服务器的地址,端口,数据
InetAddress adddress = InetAddress.getByName("localhost");
int port = 8080;
byte[] data = "用户名:admin;密码:123".getBytes();
//2.创建数据报,包含发送的数据信息
DatagramPacket packet = new DatagramPacket(data, data.length, adddress, port);
//3.创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();
//4.向服务端发送数据报
socket.send(packet);
/*
* 接收服务器响应的数据
*/
//1.创建数据报,用于接收服务器的响应数据
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
socket.receive(packet2);
String reply = new String(data2, 0, packet2.getLength());
System.out.println("我是客户端,服务器说:" + reply);
socket.close();
}
public static void main(String[] args) throws IOException {
UDPClient clint = new UDPClient();
clint.createDatagramSocket();
}
}
服务端实现的事例代码:
public class UDPserver {
public void createUDPserverScoket() throws IOException {
/*
* 接收客户端发送的数据
*/
//1.创建服务器端的DatagramSocket ,指定端口,
DatagramSocket socket = new DatagramSocket(8080);
//2.创建数据报,用于接收客户端发送的数据
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
//3.接收客户端发送过来的数据报
System.out.println("***服务器已经启动,等带客户端***");
socket.receive(packet);
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器,客户端说:" + info);
/*
* 响应客户端数据
*/
//1.定义客户端的地址,端口,数据
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎您!".getBytes();
//2.创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
//3.响应客户端
socket.send(packet2);
socket.close();
}
public static void main(String[] args) throws IOException {
UDPserver server = new UDPserver();
server.createUDPserverScoket();
}
}
经验与技巧:
- 对于同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不要关闭流,关闭socket即可
- 多线程中,未设置优先级可能会导致运行时输出非常慢,可降低优先级
thread.setPriority(4) //设置线程的优先级,范围为[1,10],默认为5
- 使用TCP通信传递对象
ObjetctOutputStream oos = new ObjectOutputStream(os);
User user = new User("admin", "1234");
oos.writeObject(user);
- sockete编程传递文件