网络编程基础
网络编程基础
一、网络的相关概念
1.网络通信
2.网络
网络编程的目的:直接或间接地通过网络协议与其他计算机实现数据交换进行通讯
要想实现网络编程,需要关注两个问题:
1.如何准确定位网络上的一台或多台主机,定位主机上的特定应用
2.找到主机以后怎么进行可靠高效的数据传输
3 .IP地址
IP地址就是网络上定位的一台主机,端口号就是定位这台主机是哪一个应用在进行通信,端口号用来区分一台主机上面的不同应用程序
4.ipv4地址分类
5.域名和端口号
比如说我们想要访问一个网站,由于IP地址难以记忆,我们一般在浏览器上写的是域名,域名经过DNS域名解析服务器,会把域名解析出来,看它的IP是多少,解析完以后,再拿这个IP地址去访问对应的服务器,就可以把这个资源访问到了
不同的进程对应不同的端口号
端口号与IP地址的组合得出一个网络套接字:Socket
一个服务要接收和发送数据的话,需要有一个端口。端口就类似于人的耳朵,我们要听懂别人的话,必须要有耳朵
6.网络通信协议
OSI模型过于理想化,没有被广泛推广
7. TCP和UDP
TCP类似于打电话
UDP类似于发短信
TCP三次握手
举一个生活中的小例子:打电话
打电话的时候,假设一个叫tom,一个叫kim,tom想要打电话和kim说事情,这个时候,电话必须打通,才可以传递信息,就类似于TCP连接。为了确保kim有在听,tom在传递信息之前先问一句,你在听吗?kim听到后就回说在听,然后tom就说我要开始说事情了,紧接着开始说事情
UDP协议无法保证传输的数据一定被接收到
UDP协议就是没有确认对方是否能够接收到消息,就直接传输信息
比如说:你发送信息给别人,但是这个电话号码可能停机了或者说注销了,你不能确保别人能接收消息,类似于UDP协议
假如说现在有一个人也想要打电话给kim,由于kim这个时候在和tom打电话,他们两个如果电话没有挂掉,这个人的电话是打不通的,就类似于TCP协议要释放已经建立的连接
tom发信息给kim,另外一个人同样也可以发信息给kim,就类似于UDP协议不需要释放资源
二、InetAddress 类
一个InetAddress类的对象就当于是一个IP地址
//File file=new File(“hello.txt”);
//比如说在这里file就对应内存中的hello.txt这个文件
//类似于这个
//inet1就对应一个具体的IP地址
InetAddress inet1=InetAddress.getByName(“192.168.10.14”);
//写上主机名就可以返回对应的对象
1.相关方法
getByName(String host) 、 getLocalHost()
两个常用方法:getHostName() 获取域名 / getHostAddress() 获取主机地址
2.实例操作
package com.zyh.java;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**InetAddress类的使用
* @author zengyihong
* @create 2022--02--27 8:46
*/
public class InetAddressMethod {
public static void main(String[] args) throws UnknownHostException {
//获取本机的InetAddress对象
InetAddress localHost = InetAddress.getLocalHost();
//计算机名称和识别到的ip地址
System.out.println(localHost);
//我们也可以根据主机名来获取InetAddress对象
InetAddress byName = InetAddress.getByName("这里写上你的主机名");
System.out.println("host="+byName);
//根据域名来返回InetAddress对象
InetAddress byName1 = InetAddress.getByName("www.baidu.com");
System.out.println("百度:"+byName1);
//根据InetAddress对象获取对呀的主机地址
String hostAddress = byName1.getHostAddress();
System.out.println(hostAddress);//ip地址
//通过InetAddress对象,获取对应的主机名或者域名
String hostName = byName1.getHostName();
System.out.println(hostName);
}
}
三、Socket
Socket就类似于通信的通道,发送数据是通过socket
两台主机要通信首先要建立连接,就相当于是建立一个数据通道
客户端发起连接,服务器端接收连接就会形成这样一个数据通道
Socket有两个编程方式:TCP编程和UDP编程
1.TCP网络通信编程
1.1基本介绍
Socket最后记得关闭,不然连接越来越多,最后就导致无法连接
1.2案例1:客户端发送信息给服务端
思路:
ServerSocket可以有多个Socket,也就是说服务器端可以和多个客户端连接,否则就不能处理多并发问题
服务器端:
package com.zyh.java;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**服务器端
* @author zengyihong
* @create 2022--02--27 9:25
*/
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//在本机的9999端口监听,等待连接
//要求:本机没有其他服务监听这个端口
//ServerSocket可以有多个Socket 通过accept可以返回多个Socket
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务器端在9999端口等待连接...");
//如果没有客户端连接此端口,程序就会阻塞,等待连接
//如果有客户端连接,则返回Socket对象
Socket socket = serverSocket.accept();
//没有连接的话,下面的代码都不会运行
System.out.println("服务器端 socket="+socket.getClass());
System.out.println("服务器端开始接收信息");
//连接以后要读取客户端发送的信息
//通过IO读取
InputStream inputStream = socket.getInputStream();
byte[] bytes=new byte[10];
int len;
while ((len=inputStream.read(bytes))!=-1){
String s = new String(bytes, 0, len);
System.out.print (s);
}
//关闭流和socket
inputStream.close();
socket.close();
serverSocket.close();
}
}
客户端:
package com.zyh.java;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
/**
* 客户端
*
* @author zengyihong
* @create 2022--02--27 9:31
*/
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//连接服务器端 (ip,端口)
//连接某一台主机的9999端口,如果连接成功,返回socket对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket返回="+socket.getClass());
//连接上后,生成Socket,发送数据给服务端,通过socket,getOutputStream()
//得到和socket对象关联的输出流对象
System.out.println("客户端要开始发消息给服务端了");
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,server".getBytes());
//关闭流对象和socket
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
运行效果:
在服务器端:
服务器端在9999端口等待连接...
服务器端 socket=class java.net.Socket
hello,server
在客户端:
客户端 socket返回=class java.net.Socket
客户端退出
1.3案例2:客户端发信息给服务器端,并接收服务器端发回来的消息
注意:服务器端发消息给客户端的时候,客户端并不知道服务器端什么时候发送信息结束,也就是发送信息的时候,要有一个结束标记,不然就会一直等待,同样的,另外一个发送信息的时候,发送结束也应该要有一个结束标记,不然就不知道对方还要不要继续发送信息
服务器端:
package com.zyh.java;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**服务器端
* @author zengyihong
* @create 2022--02--27 9:25
*/
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//在本机的9999端口监听,等待连接
//要求:本机没有其他服务监听这个端口
//ServerSocket可以有多个Socket 通过accept可以返回多个Socket
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务器端在9999端口等待连接...");
//如果没有客户端连接此端口,程序就会阻塞,等待连接
//如果有客户端连接,则返回Socket对象
Socket socket = serverSocket.accept();
//没有连接的话,下面的代码都不会运行
System.out.println("服务器端 socket="+socket.getClass());
System.out.println("服务器端开始接收信息");
//连接以后要读取客户端发送的信息
//通过IO读取
InputStream inputStream = socket.getInputStream();
byte[] bytes=new byte[10];
int len;
while ((len=inputStream.read(bytes))!=-1){
String s = new String(bytes, 0, len);
System.out.print (s);
}
//获取 socket 相关联的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello, client".getBytes());
// 设置结束标记
socket.shutdownOutput();
//关闭流和socket
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
客户端:
package com.zyh.java;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
/**
* 客户端
*
* @author zengyihong
* @create 2022--02--27 9:31
*/
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
//连接服务器端 (ip,端口)
//连接某一台主机的9999端口,如果连接成功,返回socket对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket返回="+socket.getClass());
//连接上后,生成Socket,发送数据给服务端,通过socket,getOutputStream()
//得到和socket对象关联的输出流对象
System.out.println("客户端要开始发消息给服务端了");
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,server".getBytes());
// 设置结束标记
socket.shutdownOutput();
// 获取和 socket 关联的输入流. 读取数据(字节) ,并显示
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != - 1) {
System.out.println(new String(buf, 0, readLen));
}
//关闭流对象和socket
inputStream.close();
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
运行效果:
在服务器端:
```xml
服务器端在9999端口等待连接...
服务器端 socket=class java.net.Socket
hello,server
在客户端:
客户端 socket返回=class java.net.Socket
hello,client
客户端退出
1.4案例3:使用字符流
客户端
package com.hspedu.socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
@SuppressWarnings({"all"})
public class SocketTCP03Server {
public static void main(String[] args) throws IOException {
//思路
//1. 在本机 的 9999 端口监听, 等待连接
// 细节: 要求在本机没有其它服务在监听 9999
// 细节: 这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发] ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在 9999 端口监听,等待连接..");
//2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
// 如果有客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务端 socket =" + socket.getClass());
//
//3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
InputStream inputStream = socket.getInputStream();
//4. IO 读取, 使用字符流, 使用 InputStreamReader 将 inputStream 转成字符流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);//输出
//5. 获取 socket 相关联的输出流
OutputStream outputStream = socket.getOutputStream();
// 使用字符输出流的方式回复信息
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello client 字符流");
bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束
bufferedWriter.flush();//注意需要手动的 flush
//6.关闭流和 socket
bufferedWriter.close();
bufferedReader.close();
socket.close();
serverSocket.close();//关闭
}
}
服务端:
package com.hspedu.socket;
import java.io.*;
import java.net.InetAddress; import java.net.Socket;
/**
*
* 客户端,发送 "hello, server" 给服务端, 使用字符流 */
@SuppressWarnings({"all"})
public class SocketTCP03Client {
public static void main(String[] args) throws IOException {
//思路
//1. 连接服务端 (ip , 端口)
//解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket 返回=" + socket.getClass());
//2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
// 得到 和 socket 对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3. 通过输出流,写入数据到 数据通道, 使用字符流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello, server 字符流");
bufferedWriter.newLine();
//插入一个换行符,表示写入的内容结束, 注意,要求对方使用 readLine()
//!!!! bufferedWriter.flush();
// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道
//4. 获取和 socket 关联的输入流. 读取数据(字符) ,并显示
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine();
System.out.println(s);
//5. 关闭流对象和 socket, 必须关闭
bufferedReader.close();//关闭外层流
bufferedWriter.close();
socket.close();
System.out.println("客户端退出.....");
}
}
运行结果:
1.5案例4:发送图片
1.6netstat指令
netstat - anb表示我们在查看程序的时候,知道是哪一个程序在占用这个端口
1.7 TCP 网络通讯不为人知的秘密
当客户端和服务器连接成功以后,客户端这里也有一个端口,它的端口是不确定的,是由TCP/IP分配的。客户端这里对应的端口和服务器端通讯
当传输结束以后,客户端的端口就被释放了
2.UDP网络编程(了解)
用得相对比较少,但是效率比较高
1. 基本介绍
2.基本流程
3.案例
package com.zyh.java.udptest;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**UDP接收端
* @author zengyihong
* @create 2022--02--27 12:38
*/
public class SocketUDP01Reciver {
public static void main(String[] args) throws IOException {
//创建一个DatagramSocket对象,准备在9999端口接收数据
DatagramSocket datagramSocket=new DatagramSocket(9999);
//创建 DatagramPacket对象,准备接收数据
//UDP数据包每一个大小被限制在64K内,不适合传输大量数据
byte[] bytes=new byte[1024];
DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length);
//调用接收方法,把通过网络传输的DatagramPacket对象
//填充到packet对象
//会在9999端口等待,如果有数据包发送到此端口,就会接收到数据
//如果没有数据包发送到9999端口,就会阻塞,一直等,直到有数据传输
System.out.println("接收端A等待接收数据...");
datagramSocket.receive(datagramPacket);
//把datagramPacket进行拆包,取出数据,并显示
//因为数据的传输被包装起来了
//返回实际接收的长度
int length = datagramPacket.getLength();
//接收到的数据
byte[] data = datagramPacket.getData();
String s = new String(data, 0, length);
System.out.println(s);
//关闭资源
datagramSocket.close();
System.out.println("A端退出");
}
}
package com.zyh.java.udptest;
import java.io.IOException;
import java.net.*;
/**UDP发送端
* 发送端B也可以接收数据
* 发送端将来也可以是接收端
* @author zengyihong
* @create 2022--02--27 12:39
*/
public class SocketUDP01Send {
public static void main(String[] args) throws IOException {
//创建DatagramSocket对象,准备发送和接收数据
//准备在9998端口接收数据
DatagramSocket socket=new DatagramSocket(9998);
//把要发送的数据封装到DatagramPacket对象 内容字节数组,长度,主机(IP),端口
byte[] buff= "hello,明天吃火锅".getBytes();
//ipconfig可以查看网络配置
DatagramPacket packet=new DatagramPacket(buff,buff.length,InetAddress.getLocalHost(),9999);
//发送数据
socket.send(packet);
//关闭资源
socket.close();
System.out.println("B端退出");
}
}
运行结果
接收端A等待接收数据...
hello,明天吃火锅
A端退出
B端退出```
- 点赞
- 收藏
- 关注作者
评论(0)