网络编程
一、了解网络
网络的概念
- 网络:一组相互连接的计算机
1、多台计算机组成。
2、使用物理线路进行连接。
二、网络编程的三要素
- IP地址:唯一标识网络上的每台计算机;是两台计算机之间通信的必备要素。
- 端口号:计算机中应用的标号:代表一个应用程序);0-1024系统使用或保留端口,有效端口0-65536.
- 通信协议:通信的规则,包含TCP和UDP。
- 网络中的每台计算机都有一个唯一的IP地址,网络通过IP地址进行通信。
- 计算机A使用QQ与外界交流,那么计算机B的使用者只能通过QQ去解析对方发来的信息,计算机中有那么多的应用程序,使用数字来对这些网络应用程序做标识,这些数字形象的称之为端口号。
- 通信之间必须使用相同的规则,这些规则叫协议,国际通用协议TCP/UDP。
网络模型
OSI参考模型
开放系统互连参考模型(Open System Interconnect)
TCP/IP参考模型
传输控制/国际协议(Transfer Controln Protocol/Internet Protocol)
IP地址
IP地址的表示方法
IP地址:32位,由4个8位二进制数组成。
IP表示方法:点分十进制。
IP地址=网络ID+主机ID
- 网络ID:标识计算机或网络设备所在的网段。
- 主机ID:标识特定主机或网络设备。
IP地址的分类
地址类用于指定网络ID并在网络ID和主机ID之间提供分隔方法。
IANA负责分配A、B、C类网络地址,具体主机地址由机构组织自行分配。
IP地址类分类:
- A类:一个A类IP地址由1字节的网络地址和3字节主机地址组成,它主要为大型网络而设计的,网络地址的最高位必须是"0", 地址范围从1.0.0.0 到127.0.0.0)。可用的A类网络有127个,每个网络能容纳16777214个主机。
- B类:一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是"10",地址范围从128.0.0.0到191.255.255.255。可用的B类网络有16382个,每个网络能容纳6万多个主机 。
- C类:一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是"110"。范围从192.0.0.0到223.255.255.255。C类网络可达209万余个,每个网络能容纳254个主机。
- D类: D类IP地址是一个专门保留的地址。它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中。多点广播地址用来一次寻址一组计算机,它标识共享同一协议的一组计算机。224.0.0.0到239.255.255.255用于多点广播 。
- E类:以"llll0"开始,为将来使用保留。240.0.0.0到255.255.255.254,255.255.255.255用于广播地址。
注:在IP地址3种主要类型里,各保留了3个区域作为私有地址,其地址范围如下:
A类地址:10.0.0.0~10.255.255.255
B类地址:172.16.0.0~172.31.255.255
C类地址:192.168.0.0~192.168.255.255
特殊的IP地址
0.0.0.0:本机。
127.0.0.1:本机回环地址,用于本机测试。
255.255.255.255:当前子网,一般用于向当前子网广播信息。
IP地址所对应的对象(InetAddress)
方法 | 描述 |
---|---|
public static InetAddress getLocalhost( ) | 获取主机名和IP地址 |
public String getHostAddress( ) | 获取IP地址 |
public String getHostName( ) | 获取主机名 |
public static InetAddress getByName(String host) | 根据主机名获得IP地址 |
示例代码:
java
public class InteAddressDemo {
public static void main(String[] args) throws UnknownHostException {
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
InetAddress inetAdd = InetAddress.getByName("www.baidu.com");
System.out.println(inetAdd);
System.out.println(inetAdd.getHostAddress());
System.out.println(inetAdd.getHostName());
}
}
/* 输出的结果是:
// 此行会输出你的主机名和IP地址我就不漏出我的主机命和IP地址了
www.baidu.com/183.2.172.42
183.2.172.42
www.baidu.com
*/
端口
端口:port
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。
传输协议
- UDP:相当于发短信(有字数限制),不需要建立连接,数据报的大小在64k内,效率较高,但不安全,容易丢包。
- TCP:相当于打电话,需要建立连接,效率相对比较低,数据传输安全,三次握手完成。(点名--》答到--》确认)
三、Socket套接字
- 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。
- Java中使用Socket完成TCP程序的开发,使用此类可以方便的建立可靠的 、双向的 、持续性的 、点对点的通讯连接。
- 在Socket的程序开发中,服务器端使用ServerSocket等待客户端的连接,对于Java的网络程序来讲,每个客户端都使用一个Socket对象表示。
基于TCP协议的Socket编程
进行网络通信时,Socket需要借助数据流来完成数据的传递工作。
演示案例: 互相传输一句话。
服务器端:
java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//创建serverSocket对象
ServerSocket serverSocket = new ServerSocket(10000);
//获取服务端的套接字对象
Socket socket = serverSocket.accept();
//--------------------接收客户端的输入---------------
//获取输入流对象
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println("客户端传输的数据是:" + new String(buf,0,length));
//------------------返回客户端数据------------------
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好,收到".getBytes());
//关闭流操作
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
// 输出的结果是:
// 客户端传输的数据是:hello java
客户端:
java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//创建客户端的套接字
Socket client = new Socket("127.0.0.1",10000);
//------------------------向外进行输出------------------
//获取输出流对象
OutputStream outputStream = client.getOutputStream();
//数据输出
outputStream.write("hello java".getBytes());
//------------------------接收服务端返回的消息-------------
//获取输入流对象
InputStream inputStream = client.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println("服务端的响应数据是:" + new String(buf,0,length));
//关闭流操作
inputStream.close();
outputStream.close();
client.close();
}
}
// 输出的结果是:
// 服务端的响应数据是:你好,收到
演示案例: 客户端向服务器端提交一张照片。
服务器端:
java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class PicServer {
public static void main(String[] args) throws IOException {
//创建服务端对象,开放端口
ServerSocket serverSocket = new ServerSocket(10086);
//创建服务器的Socket
Socket server = serverSocket.accept();
//获取输入流对象
InputStream inputStream = server.getInputStream();
//创建文件输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("2.jpg");
int temp = 0;
while ((temp = inputStream.read()) != -1){
fileOutputStream.write(temp);
}
//添加流输出完成的标志
server.shutdownInput();
//上传图片结束之后给予客户端响应
OutputStream outputStream = server.getOutputStream();
outputStream.write("上传成功".getBytes());
server.shutdownOutput();
//关闭流
outputStream.close();
fileOutputStream.close();
inputStream.close();
server.close();
serverSocket.close();
}
}
客户端:
java
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class PicClient {
public static void main(String[] args) throws Exception {
//创建图片的输入流对象
FileInputStream fileInputStream = new FileInputStream("1.jpg");
//创建Socket
Socket client = new Socket("localhost",10086);
//获取输出流对象
OutputStream outputStream = client.getOutputStream();
int temp = 0;
while ((temp = fileInputStream.read()) != -1){
outputStream.write(temp);
}
client.shutdownOutput();
//接收服务端的响应
InputStream inputStream = client.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println(new String(buf,0,length));
client.shutdownInput();
//关闭流操作
inputStream.close();
outputStream.close();
client.close();
fileInputStream.close();
}
}
注意: 要先启动服务器端再启动客户端。
Socket中实现对象的传递
如何传递对象信息呢?
java
String info = "用户名:Lisi;用户密码:123456";
outputStream.write(info.getBytes());
序列化
java
User user = new User();// User是用户类
user.setLoginName("Lisi");
user.setPassword("123456");
oos.writeObject(user);
演示案例: 实现用户登录。
- 方法一
用户类:
java
import java.io.Serial;
import java.io.Serializable;
public class User implements Serializable {
@Serial// 指定序列化版本号
private static final long serialVersionUID = -8254223107980283532L;
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
服务器端:
java
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class LoginServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(10000);
while (true){
Socket server = serverSocket.accept();
//获取输入流对象
InputStream inputStream = server.getInputStream();
//需要使用ObjectInputStream对象
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
User user = (User) objectInputStream.readObject();
String str = "";
if ("lyf".equals(user.getUsername()) && "lyf".equals(user.getPassword())){
str = "登录成功";
System.out.println("欢迎您:" + user.getUsername());
} else {
str = "登录失败";
}
//截断输入流
server.shutdownInput();
//给客户端响应
DataOutputStream dataOutputStream = new DataOutputStream(server.getOutputStream());
dataOutputStream.writeUTF(str);
server.shutdownOutput();
//关闭流操作
dataOutputStream.close();
objectInputStream.close();
inputStream.close();
server.close();
}
}
}
// 输出的结果是:
// 欢迎您:lyf
客户端:
java
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class LoginClient {
public static void main(String[] args) throws IOException {
Socket client = new Socket("localhost",10000);
OutputStream outputStream = client.getOutputStream();
//完成用户登录功能,需要传输一个user对象
User user = getUser();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(user);
//调用shutdown方法告诉对方传输完成
client.shutdownOutput();
//接收响应
DataInputStream dataInputStream = new DataInputStream(client.getInputStream());
String str = dataInputStream.readUTF();
System.out.println(str);
//关闭流操作
dataInputStream.close();
objectOutputStream.close();
outputStream.close();
client.close();
}
public static User getUser(){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
return new User(username,password);
}
}
/*
请输入用户名:
lyf
请输入密码:
lyf
输入正确的用户名和密码后会返回:登录成功
*/
- 方法二
用户类:
java
import java.io.Serial;
import java.io.Serializable;
public class User implements Serializable {
@Serial// 指定序列化版本号
private static final long serialVersionUID = -8254223107980283532L;
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
服务器端:
java
import java.net.ServerSocket;
import java.net.Socket;
public class LoginServer2 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(10000);
while (true){
Socket socket = serverSocket.accept();
LoginThread loginThread = new LoginThread(socket);
new Thread(loginThread).start();
}
}
}
// 输出的结果是:
// 欢迎您:lyf
线程类:
java
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
public class LoginThread implements Runnable{
private Socket socket;
public LoginThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
ObjectInputStream objectInputStream = null;
DataOutputStream dataOutputStream = null;
try {
objectInputStream = new ObjectInputStream(socket.getInputStream());
User user = (User) objectInputStream.readObject();
String str = "";
if ("lyf".equals(user.getUsername()) && "lyf".equals(user.getPassword())){
str = "登录成功";
System.out.println("欢迎您:" + user.getUsername());
} else {
str = "登录失败";
}
socket.shutdownInput();
dataOutputStream = new DataOutputStream(socket.getOutputStream());
dataOutputStream.writeUTF(str);
socket.shutdownOutput();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}finally {
try {
dataOutputStream.close();
objectInputStream.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
客户端:
java
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class LoginClient {
public static void main(String[] args) throws IOException {
Socket client = new Socket("localhost",10000);
OutputStream outputStream = client.getOutputStream();
//完成用户登录功能,需要传输一个user对象
User user = getUser();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(user);
//调用shutdown方法告诉对方传输完成
client.shutdownOutput();
//接收响应
DataInputStream dataInputStream = new DataInputStream(client.getInputStream());
String str = dataInputStream.readUTF();
System.out.println(str);
//关闭流操作
dataInputStream.close();
objectOutputStream.close();
outputStream.close();
client.close();
}
public static User getUser(){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
return new User(username,password);
}
}
/*
请输入用户名:
lyf
请输入密码:
lyf
输入正确的用户名和密码后会返回:登录成功
*/
上述两种方法都能实现客户端与服务器端之间的用户登录效果。
基于UDP协议的Socket编程
TCP和UDP之间的区别:
1、TCP协议通信双方需要建立连接,而UDP协议通信双方不需要建立连接。
2、TCP协议通信双方连接建立时双方存在主次之分,而UDP协议通信双方完全平等。
示例代码:
服务器端:
java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args) throws Exception {
DatagramSocket datagramSocket = new DatagramSocket(10001);
byte[] buf = new byte[1024];
//用来接收传输过来的数据
DatagramPacket datagramPacket = new DatagramPacket(buf,buf.length);
//利用创建好的数据报包对象来接收数据
datagramSocket.receive(datagramPacket);
//打印输出信息
System.out.println(new String(datagramPacket.getData(),0,datagramPacket.getLength()));
datagramSocket.close();
}
}
// 接收客户端控制台上传输过来的数据并打印
客户端:
java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UDPClient {
public static void main(String[] args) throws Exception {
//创建udp通信的socket
DatagramSocket datagramSocket = new DatagramSocket(10000);
//从控制台读取数据
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
DatagramPacket datagramPacket = new DatagramPacket(str.getBytes(),str.getBytes().length, InetAddress.getByName("localhost"),10001);
datagramSocket.send(datagramPacket);
datagramSocket.close();
}
}
// 在客户端的控制台上输入数据