day38上
网络编程
InetAddress
理解:表示主机类
一个域名 对应 多个IP地址
public static void main(String[] args) throws UnknownHostException {
//获取本机的IP地址
// InetAddress localHost = InetAddress.getLocalHost();
// System.out.println(localHost);
//获取域名对应的服务器地址
// InetAddress byName = InetAddress.getByName("www.baidu.com");
// System.out.println(byName);
//获取域名对应的所有的服务器地址
InetAddress[] allByName = InetAddress.getAllByName("www.baidu.com");
for (InetAddress inetAddress : allByName) {
System.out.println(inetAddress);
}
}
//输出:www.baidu.com/183.2.172.42
// www.baidu.com/183.2.172.185
//是可以通过服务器地址如183.2.172.42去访问的
Scoket
Scoket也叫套接字,其表示的是IP地址和端口号的组合。
网络编程主要就是指Socket编程,网络间的通信其实就是Socket间的通信,数据就通过IO流在两个Scoket间进行传递。
TCP
API:Socket,ServerSocket
客户端(发送一个请求) 服务端(接收到这个请求,给予响应)
案例
1.简单的TCP通信
- 编写服务端程序
- 编写客户端程序
- 客户端向服务端发送请求信息,服务端成功接收
- 服务端向客户端发送响应信息,客户端成功接收
2.升级服务端,使其处理多个客户端请求
3.继续优化服务端,让多个客户端的请求无需排队
4.关闭资源-提取工具类
简单的TCP通信
简单的TCP通信理解图
TCP理解图
ps:奇男子去会所按摩
简单的TCP通信
前提:在同一局域网下
先运行服务器
//服务端
public class Server {
public static void main(String[] args) throws IOException {
//大堂经理
ServerSocket server = new ServerSocket(8080);
//18号技师
//注意:accept()是线程阻塞的方法,该方法会等待客户端的连接成功后才生成一个Socket对象与之交互
Socket socket = server.accept();
//2.接受来自客户端的数据
// InputStream in = socket.getInputStream();
// InputStreamReader isr = new InputStreamReader(in, "GBK");
// BufferedReader br = new BufferedReader(isr);
//一步步封装
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
String readLine = br.readLine();
System.out.println(readLine);
//3.向客户端发送数据
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("18号技师:今年18岁");
br.close();
ps.close();
server.close();
}
}
使用println、BufferedReader
底层识别换行,BufferedReader流的readLine() – 读取一行
接收是获取的通过转换流再转带缓冲区的流,注意编码格式以防乱码
socket不能随便关闭,最后关闭或者不关闭
//客户端
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//注意:关闭流相当于关闭Socket!!!
//奇男子
Socket socket = new Socket("127.0.0.1", 8080);
//1.向服务端发送数据
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("奇男子:小妹妹,你多大了?");
//4.接受来自服务端的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
String readLine = br.readLine();
System.out.println(readLine);
ps.close();
br.close();
socket.close();
}
}
简单TCP通信运行图
注意运行显示接受信息
传输文件
类似文件拷贝
较简单TCP通信,使用流不同
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080);
Socket socket = server.accept();
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.mp4"));
byte[] bs = new byte[1024];
int len;
while((len = bis.read(bs)) != -1){
bos.write(bs, 0, len);
}
bis.close();
bos.close();
server.close();
}
}
//读取写入文件
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 8080);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("奇男子.mp4"));
//直接输出流转换为带有缓冲区的,效率更高
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] bs = new byte[1024];
int len;
while((len = bis.read(bs)) != -1){
bos.write(bs, 0, len);
}
bis.close();
bos.close();
socket.close();
}
}
//读取源文件写出
单聊
单聊
把流放在循环外面,防止每次次循环都new一个流,浪费资源,还有就是没有关闭资源
一对一,你一言我一语
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080);
Socket socket = server.accept();
Scanner scan = new Scanner(System.in);
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
PrintStream ps = new PrintStream(socket.getOutputStream());
while(true){
String readLine = br.readLine();
System.out.println(readLine);
ps.println("18号技师:" + scan.next());
}
}
}
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 8080);
Scanner scan = new Scanner(System.in);
PrintStream ps = new PrintStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
while(true){
ps.println("奇男子:" + scan.next());
String readLine = br.readLine();
System.out.println(readLine);
}
}
}
优化
单聊一言一语改进:用多线程,一个线程发送、一个线程接受
改进后一个人可以发多条消息
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080);
Socket socket = server.accept();
//接收消息
new ReceiveThread(socket).start();
//发送消息
Scanner scan = new Scanner(System.in);
PrintStream ps = new PrintStream(socket.getOutputStream());
while(true){
ps.println("18号技师:" + scan.next());
}
}
}
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 8080);
//接收消息
new ReceiveThread(socket).start();
//发送消息
Scanner scan = new Scanner(System.in);
PrintStream ps = new PrintStream(socket.getOutputStream());
while(true){
ps.println("奇男子:" + scan.next());
}
}
}
添加一个接受消息的线程
//接受的线程
public class ReceiveThread extends Thread{
private Socket socket;
public ReceiveThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
while(true){
String readLine = br.readLine();
System.out.println(readLine);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
群聊
多线程,发送在子线程,接受在主线程
设想是几个客户端之间互相发送消息,但由于这里使用TCP,而(面向连接)TCP必须通过服务端进行交流,不能这么操作
TCP群聊理解图
服务端连接成功一个就会产生一个Socket,ServerSocket不算进去
这里接受当前客户端的消息再发送给其他客户端,key就直接做成输出流,而不是Socket对象
客户端没有问题,就直接用Client、ReceiveThread,服务端需要更改
简单测试需要多个电脑操作进行群聊,即多个客户端
public class Server {
public static final ConcurrentHashMap<String, PrintStream> map = new ConcurrentHashMap<>();
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080);
while(true){
Socket socket = server.accept();
String ip = socket.getInetAddress().toString();
PrintStream ps = new PrintStream(socket.getOutputStream());
map.put(ip, ps);
new ServerThread(socket).start();
}
}
}
添加一个服务端的线程
public class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//接受当前客户端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
while(true){
String readLine = br.readLine();
System.out.println(readLine);
//发送给其他客户端消息
Set<Entry<String,PrintStream>> entrySet = Server.map.entrySet();
for (Entry<String, PrintStream> entry : entrySet) {
String ip = entry.getKey();
PrintStream ps = entry.getValue();
if(!socket.getInetAddress().toString().equals(ip)){
ps.println(readLine);
}
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三次握手、四次挥手
UDP
简介
UDP(User Datagram Protocol)用户数据报包协议,UDP和TCP位于同一层-传输层,但它对于数据包的顺序错误或重发没有TCP可靠;UDP是一种面向无连接的通信协议。UDP向应用程序提供一种发送封装的原始IP数据报的方法,并且发送时无需建立连接,不保证可靠数据的传输
UDP理解图
适用:视频聊天
简单的UDP通信
协议的Socket是DatagramSocket
trim()去首尾空格,因为传的数据不到容量就会是空白,超过容量也接收不了
public class Client01 {
public static void main(String[] args) throws IOException {
//注意:7070表示的是自己的端口号,不是对方的端口号
DatagramSocket socket = new DatagramSocket(7070);
//1.向客户端2发送数据
byte[] buf = "小桥流水人家".getBytes();
DatagramPacket p = new DatagramPacket(buf , 0, buf.length, InetAddress.getByName("127.0.0.1"), 8080);
socket.send(p);
//4.接受来自客户端1的数据
buf = new byte[1024];
p = new DatagramPacket(buf , buf.length);
socket.receive(p);
System.out.println(new String(buf).trim());
socket.close();
}
}
public class Client02 {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8080);
//2.接受来自客户端1的数据
byte[] buf = new byte[1024];
DatagramPacket p = new DatagramPacket(buf , buf.length);
socket.receive(p);
System.out.println(new String(buf).trim());
//3.向客户端2发送数据
buf = "古道西风瘦马".getBytes();
p = new DatagramPacket(buf , 0, buf.length, InetAddress.getByName("127.0.0.1"), 7070);
socket.send(p);
socket.close();
}
}
单聊
客户端之间发送消息
public class Client01 {
public static void main(String[] args) throws SocketException {
DatagramSocket socket = new DatagramSocket(7070);
new ReceiveThread(socket).start();
new SendThread("奇男子", "127.0.0.1", 8080, socket).start();
}
}
public class Client02 {
public static void main(String[] args) throws SocketException {
DatagramSocket socket = new DatagramSocket(8080);
new ReceiveThread(socket).start();
new SendThread("小小", "127.0.0.1", 7070, socket).start();
}
}
添加一个发送线程、一个接收线程
public class SendThread extends Thread{
private String nickName;
private String ip;
private int port;
private DatagramSocket socket;
public SendThread(String nickName, String ip, int port, DatagramSocket socket) {
this.nickName = nickName;
this.ip = ip;
this.port = port;
this.socket = socket;
}
@Override
public void run() {
Scanner scan = new Scanner(System.in);
while(true){
byte[] buf = (nickName + ":" + scan.next()).getBytes();
try {
DatagramPacket p = new DatagramPacket(buf, buf.length, InetAddress.getByName(ip), port);
socket.send(p);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class ReceiveThread extends Thread{
private DatagramSocket socket;
public ReceiveThread(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
while(true){
byte[] buf = new byte[1024];
DatagramPacket p = new DatagramPacket(buf , buf.length);
try {
socket.receive(p);
System.out.println(new String(buf).trim());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
TCP vs UDP
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 无面向连接 |
传输可靠性 | 可靠 | 不可靠 |
应用场合 | 传输大量数据 | 少量数据 |
速度 | 慢 | 快 |
UDP — 发短信
TCP — 打电话
HTTP
查询淘宝商品
需求:获取淘宝商品周边类别
市面上服务器一般都默认为utf-8,设置编码格式,解决乱码
public class Test01 {
public static void main(String[] args) throws IOException {
String path = "https://suggest.taobao.com/sug?code=utf-8&q=%E8%80%90%E5%85%8B&callback=cb";
//创建链接对象
URL url = new URL(path);
//获取连接对象
//玩的是他的子类,父类为抽象类
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置参数
connection.setConnectTimeout(5000);//设置连接超时时间
connection.setReadTimeout(5000);//设置读取数据超时时间
connection.setDoInput(true);//设置是否允许使用输入流
connection.setDoOutput(true);//设置是否允许使用输出流
//获取响应状态码
int code = connection.getResponseCode();
if(code == HttpURLConnection.HTTP_OK){
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
char[] cs = new char[1024];
int len;
while((len = br.read(cs)) != -1){
System.out.println(new String(cs, 0, len));
}
}else if(code == HttpURLConnection.HTTP_NOT_FOUND){
System.out.println("页面未找到");
}
}
}
下载图片
更改流即可【不同数据源用不同的流即可】
public class Test02 {
public static void main(String[] args) throws IOException {
String path = "https://wx2.sinaimg.cn/mw690/e2438f6cly1hoo3qpm7vrj21111jk4mn.jpg";
//创建链接对象
URL url = new URL(path);
//获取连接对象
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置参数
connection.setConnectTimeout(5000);//设置连接超时时间
connection.setReadTimeout(5000);//设置读取数据超时时间
connection.setDoInput(true);//设置是否允许使用输入流
connection.setDoOutput(true);//设置是否允许使用输出流
//获取响应状态码
int code = connection.getResponseCode();
if(code == HttpURLConnection.HTTP_OK){
BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("金智媛.jpg"));
byte[] bs = new byte[1024];
int len;
while((len = bis.read(bs)) != -1){
bos.write(bs, 0, len);
}
bis.close();
bos.close();
}else if(code == HttpURLConnection.HTTP_NOT_FOUND){
System.out.println("页面未找到");
}
}
}
所需学习类
DatagramPacket —此类表示数据报包
DatagramSocket —此类用来发送和接受数据报包的套接字
SocketAddress
getLocalSocketAddress
()
返回此套接字绑定的端点的地 址,如果尚未绑定则返回null
。
Void
send(
DatagramPacketp)
从此套接字发送数据报包。
Void
receive(
DatagramPacketp)
从此套接字接收数据报包。SocketAddress —抽象类,IP+端口号
总结
1.网络编程
1.1 InetAddress
1.2 TCP
传输文件
单聊、优化单聊
群聊
三次握手、四次挥手
1.3 UDP
单聊
TCP vs UDP
1.4 HTTP
查询淘宝商品
下载图片