网络编程
网络编程入门
网络编程三要素--概述
网络编程三要素---IP
在网络中我们使用ip来标识设备并通过ip来访问相应的设备。实际上我们访问网页也是依据ip,因为ip难以记忆,于是通过DNS协议
将域名转化为ip来访问相应的网页服务器
网络编程--常见命令
ping命令:1.检测你现在的这台电脑和你需要连接的电脑网络是否畅通2.确定连接电脑的ip
- 特殊的ip地址
三要素---InetAddress类的使用(对ip地址的描述)
这个类没有向外提供构造方法,但是提供了静态方法来创建对象
package com.interet;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressTest {
public static void main(String[] args) throws UnknownHostException {
/*static InetAddress getByName(String host) 确定主机名称的IP地址。
String getHostAddress() 返回文本显示中的IP地址字符串。
String getHostName() 获取此IP地址的主机名。
*/
final InetAddress address = InetAddress.getByName("LAPTOP-7KNA7AM8");//我的电脑的主机名
System.out.println(address.getHostName());//返回主机名
System.out.println(address.getHostAddress());//返回主机ip
}
}
三要素--端口
网络编程三要素--协议
- 在建立连接后才会进行发送
UDP--发送端
package com.udp;
import java.io.IOException;
import java.net.*;
//发送端
public class Client {
public static void main(String[] args) throws IOException {
//1.寻找码头
DatagramSocket ds = new DatagramSocket();//无参构造将会随机使用一个端口进行发送数据
// 2.打包数据
//参数一:数据
String data = "你好 世界!";
byte[] bytes = data.getBytes();//将字符串转化为字节数组
//参数二:接收端ip
InetAddress address = InetAddress.getByName("127.0.0.1");
//参数三:接收机的端口号
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
//3.由码头发送数据
ds.send(dp);
//4.关闭资源
ds.close();
}
}
UDP-接收端
package com.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Server {
public static void main(String[] args) throws IOException {
//1.找码头
DatagramSocket ds = new DatagramSocket(10000);
//2.创建新的箱子
byte[] bytes = new byte[1024];//字节数组用来接收传输的数据
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
//3.将数据放到新的箱子中
ds.receive(dp);//如果此时还没有接收到发送端的时候,程序会阻塞在此处
//4.从箱子中获取礼物
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(data);
//5.关闭资源
ds.close();
}
}
- 注意点:
1.要先运行接收端再运行发送端
2.如果接收端没有接收到数据,会进行死等
3.再接收数据时,需要调用一个getLength方法,表示接收到了多少字节(因为用于接收的数组没有接收满出现大量的空格)
UDP练习
package com.udppractice;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;
//客户端
/*
要求:
数据来自键盘录入,直到输入的数据是886,发送数据结束
*/
public class Client {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
DatagramSocket ds = null;
//1.准备码头
ds = new DatagramSocket();//由随机端口发出数据
//2.打包数据
while (true){
System.out.println("请输入要发送的数据:");
String data = sc.nextLine();
if("886".equals(data)){
break;
}
byte[] bytes = data.getBytes();
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,InetAddress.getByName("127.0.0.1"),10005);
//3.发送数据
ds.send(dp);
}
//4.关闭资源
ds.close();
}
}
package com.udppractice;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Server {
public static void main(String[] args) throws IOException {
//1.准备码头
DatagramSocket ds = new DatagramSocket(10005);//在10005端口中接收数据
while (true) {
//2.准备包裹
byte[] bytes = new byte[1024];//接收数据的数组
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
//3.码头接收数据并放入到包裹中
ds.receive(dp);
//4.打开包裹获取礼物
System.out.println(new String(dp.getData(),0,dp.getLength()));
}
//5.关闭资源
//ds.close();
}
}
注意:报BindException
在之前的代码中我们将 DatagramSocket ds = new DatagramSocket(10005);放在了循环中,这样在接收了第一句话后就爆出了
异常
当我们接收了第一句话使用了10005这个端口,这个端口已经被占用了,循环第二次他将认为这是地外一个程序,将会显示端口绑定异常
UDP三种通讯方式
组播代码的实现
package com.udp;
import java.io.IOException;
import java.net.*;
//组播客户端
public class MulClient {
public static void main(String[] args) throws IOException {
//1.寻找码头
DatagramSocket ds = new DatagramSocket();
//2.将数据添加到箱子里面
String data = "hello world";
final byte[] bytes = data.getBytes();
InetAddress ip = InetAddress.getByName("224.0.1.0");//地址要换成组播地址
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, ip, port);
//3.发送数据
ds.send(dp);
ds.close();
}
}
package com.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulServer {
public static void main(String[] args) throws IOException {
//1.创建码头
MulticastSocket ms = new MulticastSocket(10000);
//2.创建箱子
DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
//3."把当前计算机绑定一个组播地址,表示添加到这一组中"
ms.joinGroup(InetAddress.getByName("224.0.1.0"));
ms.receive(dp);
System.out.println(new String(dp.getData(), 0, dp.getLength()));
ms.close();
}
}
UDp-广播代码实现
广播的接收端和单播一样
package com.udp;
import java.io.IOException;
import java.net.*;
//广播-客户端
public class BroClient {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
String data = "hello 广播";
byte[] buf = data.getBytes();
InetAddress ip = InetAddress.getByName("255.255.255.255");//地址为广播地址
int port = 10000;
DatagramPacket dp = new DatagramPacket(buf, buf.length, ip, port);
ds.send(dp);
ds.close();
}
}
package com.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
//广播接收端
public class BroServer {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(10000);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
System.out.println(new String(dp.getData(), 0, dp.getLength()));
ds.close();
}
}
Tcp部分
tcp--客户端
- 客户端书写步骤
package com.tcp;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
//tcp发送端
public class Client {
public static void main(String[] args) throws IOException {
//1.创建一个Socket对象
//参数指明要发送到的主机和端口
Socket socket = new Socket("127.0.0.1",10006);
//2.创建io流并传输数据
final OutputStream outputStream = socket.getOutputStream();
//写入数据
outputStream.write("hello".getBytes());
//3.关闭资源
outputStream.close();
socket.close();
}
}
但是它会出现连接异常,因为tcp在发送数据之前会和服务端进行连接,而我们的服务端还没有写,则无法连接成功,所有出现异常
Tcp-服务器
package com.tcp;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
//tcp发送端
public class Client {
public static void main(String[] args) throws IOException {
//1.创建一个Socket对象
//参数指明要发送到的主机和端口
Socket socket = new Socket("127.0.0.1",10006);
//2.创建io流并传输数据
final OutputStream outputStream = socket.getOutputStream();
//写入数据
outputStream.write("hello".getBytes());
//3.关闭资源
outputStream.close();
socket.close();
}
}
Tcp原理分析
- 对tcp数据传输与接收的理解
Server先执行,到accept方法处阻塞等待连接,Client执行,Socket的构造方法进行三次握手进行连接。在进行数据传输时,实行一边传输一边接收。当传输和接收都完毕时才进行资源关闭
三次握手
是在客户端床架Socket对象时,通过三次握手去保证和服务端进行连接畅通
四次挥手
是客户端和服务器取消两者连接的时候,用来保证成功终止这个连接的
- 为什么连接的时候只用3次握手,但是在断开连接的时候却需要四次挥手呢?
因为在进行断开连接的时候,连接已经存在。在断开连接之前需要保证所有的数据都处理完毕。所有服务器端需要在数据处理完毕
后发送一个确认断开的信号
Tcp-练习1
- 理想写法
package com.tcpprictice;
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 {
//1.创建Socket对象
Socket socket = new Socket("127.0.0.1", 10001);
//2.获取输出流,并写入数据
OutputStream out = socket.getOutputStream();
out.write("hello".getBytes());
//接收反馈
final InputStream in = socket.getInputStream();
int b ;
while ((b = in.read()) != -1) {
System.out.print((char) b);
}
//3.关闭资源
in.close();
out.close();
socket.close();
}
}
package com.tcpprictice;
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 {
//1.获取Socket对象
ServerSocket server = new ServerSocket(10001);
//2.获取输入流,并读取数据
//等待连接
Socket socket = server.accept();//接收数据
InputStream in = socket.getInputStream();//获取输入流
int b;
while ((b = in.read()) != -1) {
System.out.print((char) b);
}
//给出反馈
final OutputStream outputStream = socket.getOutputStream();
outputStream.write("你谁啊".getBytes());
//3.关闭资源
outputStream.close();
in.close();
socket.close();
server.close();
}
}
- 运行结果
服务端接收到hello,客户端并没有接受到反馈 - 问题分析
这个结果本质上还是:
- 解决方案--提前关流
我们的关流方法,在关闭流的同时也关闭了Socket的使用。是否有一个方法可以仅仅关闭流,而不关闭SOcket
package com.tcpprictice;
import java.io.*;
import java.net.Socket;
import static java.lang.System.in;
//发送数据,并接收服务器的反馈
public class Client {
public static void main(String[] args) throws IOException {
//1.创建Socket对象
Socket socket = new Socket("127.0.0.1", 10001);
//2.获取输出流,并写入数据
OutputStream out = socket.getOutputStream();
out.write("hello".getBytes());
socket.shutdownOutput();//仅仅关闭流,并写一个结束标记(不会关闭socket)
//等待反馈
//接收反馈(接收的数据为中文,用字节流将会乱码)
/* final InputStream in = socket.getInputStream();
int b ;
while ((b = in.read()) != -1) {
System.out.print((char) b);
}*/
//使用转换流接收中文
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while (null != (line = br.readLine())) {
System.out.println(line);
}
//3.关闭资源
br.close();
out.close();
socket.close();
}
}
package com.tcpprictice;
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 {
//1.获取Socket对象
ServerSocket server = new ServerSocket(10001);
//2.获取输入流,并读取数据
//等待连接
Socket socket = server.accept();//接收数据
InputStream in = socket.getInputStream();//获取输入流
int b;
while ((b = in.read()) != -1) {
System.out.print((char) b);
}
//给出反馈(也可以换成转换流)
final OutputStream outputStream = socket.getOutputStream();
outputStream.write("你谁啊".getBytes());
//3.关闭资源
outputStream.close();
in.close();
socket.close();
server.close();
}
}
Tcp-练习2
-
该处的代码见<<java网络编程部分》
后面多线程优化的部分见<<java网络编程>>部分 -
总结
多线程的处理,可以优化多个客户端同时访问的处理,增加处理速度,可以发送数据多次