网络编程笔记
网路编程的主要问题
- 如何准确的定位到网络上的一台或者多台主机。
- 找到主机后如何进行通信。
要素
-
IP 和 端口号
-
网络通信协议:UDP TCP
ip和端口
/*
InetAddress没有构造方法,所以直接调用他的静态方法使用
*/
public static void main(String[] args) {
try {
//查询本机IP地址的方法
InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress1);
InetAddress inetAddress2 = InetAddress.getByName("localhost");
System.out.println(inetAddress2);
InetAddress inetAddress3 = InetAddress.getLocalHost();
System.out.println(inetAddress3);
//查询网络ip
InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress4);
//常用方法
System.out.println("-------方法-------");
System.out.println(inetAddress4.getAddress());//获取的是一个字节数组
System.out.println(inetAddress4.getCanonicalHostName());//获取规范的名字
System.out.println(inetAddress4.getHostAddress());//ip
System.out.println(inetAddress4.getHostName());//域名,如果是自己电脑就是计算机的名字
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
-
端口
端口就是和一个程序对应的,每个程序都有自己的端口号,通过这个端口号可以和这个程序进行通信。
规定为0- 65536
-
传输层的协议:TCP、UDP,单个端口下端口号不能冲突。
public static void main(String[] args) {
InetSocketAddress socketAddress1 = new InetSocketAddress("127.0.0.1", 8080);
InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 8080);
System.out.println(socketAddress1);
System.out.println(socketAddress2);
System.out.println(socketAddress1.getPort());//获取端口号
}
通信协议
TCP:
TCP协议全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的RFC 793定义。TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,你可以把它想象成排水管中的水流。(就是说需要建立连接).
三次握手,四次挥手:
第一次握手
客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。
四次挥手
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间.
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
————————————————
UDP:无连接的协议。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。
TCP建立通信
关键:serverSocket类用于服务端创建端口,而客户端通过InetAddress的静态方法设置需要访问的主机也就是服务器的ip,然后客户端通过Socket类创建连接。
-
步骤:
- 建立服务端和客户端
- 服务端:1.建立端口serverSocket。2.等待用户的连接(监听用户的连接)accept。3.接收客户端的消息。
- 客户端:1.连接到服务器端 Socket。2.发送消息。
-
代码测试:
1.发送消息给服务端并打印
public class DemoClient01 {
public static void main(String[] args) throws IOException {
OutputStream outputStream = null;
Socket socket = null;
try {
//1.先找到服务端的地址也就是ip(这里是本机所以是127.0.0.1)
InetAddress addressIP = InetAddress.getByName("127.0.0.1");
//2.创建一个Socket连接端口
int port = 9999;
socket = new Socket(addressIP, port);
//3.发送消息
outputStream = socket.getOutputStream();
outputStream.write("哈哈哈哈哈哈哈哈哈哈!!!".getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
outputStream.close();
socket.close();
}
}
}
/**
* @author:Mr.Han
* @description:客户端
* @date:2020/4/12
*/
public class DemoServer01 {
public static void main(String[] args) throws IOException {
//1.创建一个端口号
ServerSocket serverSocket = new ServerSocket(9999);
//2.监听客户端的socket(这里的sorket就是客户端的)
Socket socket = serverSocket.accept();
//3.接收消息
InputStream msg = socket.getInputStream();
//4.打印消息
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();//管道流
byte[] buffer = new byte[1024];
int len = 0;
while((len=(msg.read(buffer)))!=-1){
//将接收到的消息全写进ByteArrayOutputStream
outputStream.write(buffer);
}
System.out.println(outputStream.toString());
outputStream.close();
msg.close();
socket.close();
}
}
2。测试发送文件
/**
* @author:Mr.Han
* @description:服务端接收文件并输出到文件夹
* @date:2020/4/12
*/
public class DemoServer02 {
public static void main(String[] args) throws Exception {
//建立端口
ServerSocket serverSocket = new ServerSocket(9999);
//监听
Socket socket = serverSocket.accept();
//获取内容
InputStream is = socket.getInputStream();
//将获取的文件写出到文件夹
FileOutputStream fos = new FileOutputStream("recive.jpg");
int len = 0;
byte[] buffer = new byte[1024];
while ((len=is.read(buffer))!=-1){
fos.write(buffer);
}
//告诉client接收完毕
OutputStream outputStream = socket.getOutputStream();
outputStream.write("接收完毕哈哈哈!".getBytes());
outputStream.close();
fos.close();
is.close();
socket.close();
}
}
/**
* @author:Mr.Han
* @description:客户端发送文件
* @date:2020/4/12
*/
public class DemoClient02 {
public static void main(String[] args) throws Exception {
//创建与服务器的连接socket
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
//获取文件
FileInputStream fis = new FileInputStream(new File("C:\\Users\\M.han\\IdeaProjects\\JUCstudy\\src\\test\\java\\net\\book.jpg"));
//发送文件
OutputStream outputStream = socket.getOutputStream();
int len;
byte[] buffer = new byte[1024];
while((len=fis.read(buffer))!=-1){
outputStream.write(buffer);
}
//通知服务器结束了传输
socket.shutdownOutput();
//接收服务器接收完返回来的消息
InputStream inmsg = socket.getInputStream();
byte[] buffer2 = new byte[1024];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while ((len=inmsg.read(buffer2))!=-1){
byteArrayOutputStream.write(buffer2);
}
System.out.println("服务器消息:"+byteArrayOutputStream.toString());
byteArrayOutputStream.close();
inmsg.close();
outputStream.close();
fis.close();
}
}
UDP通信
关键:DatagramSocket类用于创建socket,DatagramPacket类用于创建需要发送的包或者接收的包。
UDP不需要创建端口,但是需要注意发送方和接收方。
/**
* @author:Mr.Han
* @description:udp方式通信
* @date:2020/4/12
*/
public class UDPDemo01 {
public static void main(String[] args) throws Exception {
//1.建立socket
DatagramSocket datagramSocket = new DatagramSocket();
//2.建立包
String str = "哈哈哈哈哈服务器!!";
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9999;
DatagramPacket packet = new DatagramPacket(str.getBytes(), 0, str.getBytes().length, localhost, port);
//3.发送包
datagramSocket.send(packet);
datagramSocket.close();
}
}
/**
* @author:Mr.Han
* @description:udp方式通信
* @date:2020/4/12
*/
public class UDPDemo02 {
public static void main(String[] args) throws Exception {
//建立Socket
DatagramSocket datagramSocket = new DatagramSocket(9999);
//建立接收包
byte[] buffer = new byte[1024];//最多只能接收这么多字节的内容
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//接收
datagramSocket.receive(packet);
//获取包的内容
System.out.println(packet.getAddress());
System.out.println(new String(packet.getData()));
datagramSocket.close();
}
}
发送消息
/**
* @author:Mr.Han
* @description:用户1
* @date:2020/4/12
*/
public class Chat01 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(9999);
Scanner input = new Scanner(System.in);
while(true){
//创建发送包
String data = input.nextLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, InetAddress.getByName("localhost"), 9090);
socket.send(packet);
if ("bye".equals(data)){
break;
}
}
input.close();
socket.close();
}
}
/**
* @author:Mr.Han
* @description:用户2,只能接收
* @date:2020/4/12
*/
public class Chat02 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(9090);
while (true){
//创建接收包
byte[] datas = new byte[1024];
DatagramPacket packet = new DatagramPacket(datas,0,datas.length);
socket.receive(packet);
//将内容转字符串
String str = new String(packet.getData(),0,packet.getData().length).trim();//trim去掉字符串后边的空格
System.out.println(packet.getAddress() + ":" + str);
//终止聊天
if ("bye".equals(str)){
break;
}
}
socket.close();
}
}
线程实现相互聊天
/*
思路:创建两个线程类,用于发消息和接收消息。
*/
public class Send implements Runnable {
DatagramSocket socket = null;
Scanner input = null;
InetAddress host = null;
int toPort;
int fromPort;
public Send(int toPort,InetAddress host ,int fromPort) {
this.toPort = toPort;
this.host = host;
this.fromPort = fromPort;
try {
socket = new DatagramSocket(fromPort);
input = new Scanner(System.in);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
//创建发送包
String data = input.nextLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, host, toPort);
socket.send(packet);
if ("bye".equals(data)){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
public class Recieve implements Runnable{
DatagramSocket socket = null;
int rePort;
String name;
public Recieve(int report,String name) {
this.rePort = report;
this.name = name;
try {
socket = new DatagramSocket(report);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
//创建接收包
byte[] datas = new byte[1024];
DatagramPacket packet = new DatagramPacket(datas,0,datas.length);
socket.receive(packet);
//将内容转字符串
String str = new String(packet.getData(),0,packet.getData().length).trim();//trim去掉字符串后边的空格
System.out.println(this.name+ ":" + str);
//终止聊天
if ("bye".equals(str)){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
/*
注意UDP发送的特点,不需要和对方建立联系,直接发送对方就能收到。
*/
//老师类
public static void main(String[] args) throws UnknownHostException {
//老师发送到学生接收端口9090
new Thread(new Send(9090, InetAddress.getByName("localhost"),3333)).start();
new Thread(new Recieve(9999,"学生")).start();//老师负责接收的端口是9999,收的是学生的消息
}
//学生类
public static void main(String[] args) throws UnknownHostException {
//学生发送到老师的接收端口9999
new Thread(new Send(9999, InetAddress.getByName("localhost"),2222)).start();
new Thread(new Recieve(9090,"老师")).start();//学生负责接收的端口是9090,收的老师的消息
}
下载网络资源URL
public class URLDemo {
public static void main(String[] args) {
InputStream is = null;
HttpURLConnection connection = null;
FileOutputStream fos = null;
try {
URL url = new URL("https://........");
connection = (HttpURLConnection) url.openConnection();
is = connection.getInputStream();
fos = new FileOutputStream("1.jpg");
int len;
byte[] buffer = new byte[1024];
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
connection.disconnect();
}
}
}
URL url = new URL("https://........");
System.out.println("端口-"+url.getPort());
System.out.println("主机ip-"+url.getHost());
System.out.println("文件-"+url.getPath());
System.out.println("全路径-"+url.getFile());
System.out.println("参数-"+url.getQuery());
System.out.println("协议-"+url.getProtocol());