Java学习笔记 第十章 网络编程
JAVA学习笔记第十章
10.网络编程
10.1引入
TCP建立连接:
释放连接:
10.2InetAddress & InetSocketAddress
InetAddress有两个方法:
InetSocketAddress封装了ip和端口号
10.3套接字
10.4TCP通信
10.4.1客户端和服务端通信
客户端发送一句话到服务器
public class TestClient { // 客户端
public static void main(String[] args) throws IOException {
// 创建套接字:指定服务器ip和端口号
Socket s = new Socket("127.0.0.1", 8888);
//对程序员来说,向外发送数据 感受 --》 利用输出流;
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
//利用OutputStream就可以向外发送数据流,但是没有直接发送String的方法
//所以我们又在OutputStream外面套了一个处理流:DataOutputStream
dos.writeUTF("你好");
// 关闭流 + 关闭网络资源
dos.close();
os.close();
s.close();
}
}
public class TestServer { // 服务器
public static void main(String[] args) throws IOException {
// 1.创建套接字:指定服务器的端口
ServerSocket ss = new ServerSocket(8888);
// 2.等待客户端发送来的信息
Socket s = ss.accept(); // 阻塞方法:等待客户端的数据,什么时候收到数据,什么时候程序继续向下执行
// 3.accept返回值为Socket,这个Socket其实就是客户端的Socket
//接到这个Socket之后,客户端和服务端才真正产生了连接,才真正可以通信了
//感受到的操作流
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
// 4.读取客户端发来的数据
String str = dis.readUTF();
System.out.println("客户端发来的数据为:" + str);
// 5.关闭流 + 关闭网络资源
dis.close();
is.close();
s.close();
}
}
先开服务器,然后开启客户端,在服务器端可以看到控制台打印出客户端发来的数据为:你好
如果先开客户端,会报出连接被拒绝的错误
服务端也可以向客户端发送信息
public class TestServer { // 服务器
public static void main(String[] args) throws IOException {
// 1.创建套接字:指定服务器的端口
ServerSocket ss = new ServerSocket(8888);
// 2.等待客户端发送来的信息
Socket s = ss.accept(); // 阻塞方法:等待客户端的数据,什么时候收到数据,什么时候程序继续向下执行
// 3.accept返回值为Socket,这个Socket其实就是客户端的Socket
//接到这个Socket之后,客户端和服务端才真正产生了连接,才真正可以通信了
//感受到的操作流
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
// 4.读取客户端发来的数据
String str = dis.readUTF();
System.out.println("客户端发来的数据为:" + str);
// 6.向客户端发送数据
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("服务器端接收到你的消息");
// 5.关闭流 + 关闭网络资源
dos.close();
os.close();
dis.close();
is.close();
s.close();
}
}
public class TestClient { // 客户端
public static void main(String[] args) throws IOException {
// 创建套接字:指定服务器ip和端口号
Socket s = new Socket("127.0.0.1", 8888);
//对程序员来说,向外发送数据 感受 --》 利用输出流;
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
//利用OutputStream就可以向外发送数据流,但是没有直接发送String的方法
//所以我们又在OutputStream外面套了一个处理流:DataOutputStream
dos.writeUTF("你好");
//客户端接收服务器的数据
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
String str = dis.readUTF();
System.out.println("服务器对我说:" + str);
// 关闭流 + 关闭网络资源
dis.close();
is.close();
dos.close();
os.close();
s.close();
}
}
10.4.2登录验证
模拟登录时输入账号密码,并进行验证的过程
public class User implements Serializable {
private static final long serialVersionUID = 6158646660109509935L;
private String name;
private String pwd;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
}
需要实现Serializable接口,并且通过idea创建UID
public class Client { //客户端
public static void main(String[] args) throws IOException {
// 1.创建套接字
Socket s = new Socket("127.0.0.1", 8888);
// 2.录入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("username:");
String name = sc.next();
System.out.println("password:");
String pwd = sc.next();
// 3.将账号和密码封装为一个User对象
User user = new User(name, pwd);
// 4.向外发送对象,利用输出流
OutputStream os = s.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
// 5.接收服务端发送的结果
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
Boolean flag = dis.readBoolean();
System.out.println(flag);
// 6.关闭流 + 网络资源
dis.close();
is.close();
oos.close();
os.close();
s.close();
}
}
public class Server { //服务端
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1.创建套接字
ServerSocket ss = new ServerSocket(8888);
// 2.等待客户端发来的信息
Socket s = ss.accept();
// 3.获取传入的数据
InputStream is = s.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
// 4.读取客户端发送的数据
User user = (User) ois.readObject();
// 5.对对象进行验证:
boolean flag = false;
if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
flag = true;
}
// 6.向客户端输出结果
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeBoolean(flag);
// 7.关闭流 + 网络资源
dos.close();
os.close();
ois.close();
is.close();
ss.close();
}
}
10.4.3完整的异常处理方式
Socket提出去,其他的流在外面初始化为null
关闭流和资源放到finally,再对里面的流进行异常处理
public class Client { //客户端
public static void main(String[] args){
// 1.创建套接字
Socket s = null;
OutputStream os = null;
ObjectOutputStream oos = null;
InputStream is = null;
DataInputStream dis = null;
try {
s = new Socket("127.0.0.1", 8888);
// 2.录入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("username:");
String name = sc.next();
System.out.println("password:");
String pwd = sc.next();
// 3.将账号和密码封装为一个User对象
User user = new User(name, pwd);
// 4.向外发送对象,利用输出流
os = s.getOutputStream();
oos = new ObjectOutputStream(os);
oos.writeObject(user);
// 5.接收服务端发送的结果
is = s.getInputStream();
dis = new DataInputStream(is);
Boolean flag = dis.readBoolean();
System.out.println(flag);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6.关闭流 + 网络资源
try {
if(dis != null) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class Server {
public static void main(String[] args){
// 1.创建套接字
ServerSocket ss = null;
InputStream is = null;
ObjectInputStream ois = null;
OutputStream os = null;
DataOutputStream dos = null;
try {
ss = new ServerSocket(8888);
// 2.等待客户端发来的信息
Socket s = ss.accept();
// 3.获取传入的数据
is = s.getInputStream();
ois = new ObjectInputStream(is);
// 4.读取客户端发送的数据
User user = null;
try {
user = (User) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 5.对对象进行验证:
boolean flag = false;
if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
flag = true;
}
// 6.向客户端输出结果
os = s.getOutputStream();
dos = new DataOutputStream(os);
dos.writeBoolean(flag);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 7.关闭流 + 网络资源
try {
if(dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
10.4.4多个客户端和一个服务器
客户端的代码不变
public class Client { //客户端
public static void main(String[] args){
// 1.创建套接字
Socket s = null;
OutputStream os = null;
ObjectOutputStream oos = null;
InputStream is = null;
DataInputStream dis = null;
try {
s = new Socket("127.0.0.1", 8888);
// 2.录入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("username:");
String name = sc.next();
System.out.println("password:");
String pwd = sc.next();
// 3.将账号和密码封装为一个User对象
User user = new User(name, pwd);
// 4.向外发送对象,利用输出流
os = s.getOutputStream();
oos = new ObjectOutputStream(os);
oos.writeObject(user);
// 5.接收服务端发送的结果
is = s.getInputStream();
dis = new DataInputStream(is);
Boolean flag = dis.readBoolean();
System.out.println(flag);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6.关闭流 + 网络资源
try {
if(dis != null) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务端 Server.java
public class Server {
public static void main(String[] args){
// 1.创建套接字
ServerSocket ss = null;
Socket s = null;
int count = 0; // 定义一个计数器,用来计数 客户端的请求
try {
ss = new ServerSocket(8888);
// 2.等待客户端发来的信息
while (true) { // 加入死循环,服务器一直监听客户端是否发送数据
s = ss.accept();
//每次过来的客户端的请求靠线程处理
new ServerThread(s).start();
count++;
System.out.println("当前是第"+count+"个用户访问我们的服务器,对应的用户是"+s.getInetAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
新建ServerThread.java
public class ServerThread extends Thread{ // 线程:专门处理客户端的请求
InputStream is = null;
ObjectInputStream ois = null;
OutputStream os = null;
DataOutputStream dos = null;
Socket s = null;
public ServerThread(Socket s){
this.s = s;
}
@Override
public void run() {
// 3.获取传入的数据
try{
is = s.getInputStream();
ois = new ObjectInputStream(is);
// 4.读取客户端发送的数据
User user = null;
try {
user = (User) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 5.对对象进行验证:
boolean flag = false;
if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
flag = true;
}
// 6.向客户端输出结果
os = s.getOutputStream();
dos = new DataOutputStream(os);
dos.writeBoolean(flag);
} catch (IOException e){
e.printStackTrace();
} finally {
try {
if(dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
10.5UDP通信
10.5.1单向通信
public class TestSend {
public static void main(String[] args) throws IOException { //发送方
System.out.println("学生上线");
// 1.准备套接字,指定发送方的端口号
DatagramSocket ds = new DatagramSocket(8888);
// 2.准备数据包
String str = "你好";
byte[] bytes = str.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 9999);
/*
需要四个参数: 1.传送数据转为字节的数组 2.字节数组的长度 3.接收方的IP地址 4.接收方的端口号
*/
// 3.发送
ds.send(dp);
// 4.关闭资源
ds.close();
}
}
public class TestReceive {
public static void main(String[] args) throws IOException { //接收方
System.out.println("接收消息");
// 1.准备套接字
DatagramSocket ds = new DatagramSocket(9999);
// 2.有一个空的数据包,准备接收对方传过来的数据
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
// 3.接收对方的数据包,然后放入到我们的dp数据包中填充
ds.receive(dp); //接收完以后,dp里面就填充好内容了
// 4.取出数据:
byte[] data = dp.getData();
String s = new String(data, 0, dp.getLength()); // dp.getLength()数组包中的有效长度
System.out.println(s);
// 5.关闭资源
ds.close();
}
}
10.5.2双向通信
public class TestReceive {
public static void main(String[] args) throws IOException { //接收方
System.out.println("接收消息");
// 1.准备套接字
DatagramSocket ds = new DatagramSocket(9999);
// 2.有一个空的数据包,准备接收对方传过来的数据
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
// 3.接收对方的数据包,然后放入到我们的dp数据包中填充
ds.receive(dp); //接收完以后,dp里面就填充好内容了
// 4.取出数据:
byte[] data = dp.getData();
String s = new String(data, 0, dp.getLength()); // dp.getLength()数组包中的有效长度
System.out.println(s);
// 5.回复
String str = "Hello";
byte[] bytes = str.getBytes();
DatagramPacket dp1 = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 8888);
ds.send(dp1);
// 5.关闭资源
ds.close();
}
}
public class TestSend {
public static void main(String[] args) throws IOException { //发送方
System.out.println("学生上线");
// 1.准备套接字,指定发送方的端口号
DatagramSocket ds = new DatagramSocket(8888);
// 2.准备数据包
String str = "你好";
byte[] bytes = str.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 9999);
/*
需要四个参数: 1.传送数据转为字节的数组 2.字节数组的长度 3.接收方的IP地址 4.接收方的端口号
*/
// 3.发送
ds.send(dp);
// 4.接收
byte[] b = new byte[1024];
DatagramPacket dp1 = new DatagramPacket(b, b.length);
ds.receive(dp1);
byte[] data = dp1.getData();
String s = new String(data, 0, dp1.getLength());
System.out.println(s);
// 4.关闭资源
ds.close();
}
}
10.5.3完整的异常处理方式
与TCP协议的通信一致,如10.4.3