InetAddress类
- IP地址的代表类是谁?
- InetAddress类
- 如何获取本机IP对象
- public static InetAddress getLocalHost();
- 如何判断与该IP地址对象是否互通
- public boolean isReachable(int timeout);
测试代码:
package com.h;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
/*
* 网络编程
* */
public class InetAddressDemo01 {
public static void main(String[] args) throws IOException {
//1、 获取本机地址对象
InetAddress address = InetAddress.getLocalHost();
//2、获取本地主机名与地址
System.out.println(address.getHostName());
System.out.println(address.getHostAddress());
//3、获取域名ip地址,前提是电脑联网或者有dns缓存
InetAddress address1 = InetAddress.getByName("www.baidu.com");
System.out.println(address1.getHostAddress());
System.out.println(address1.getHostName());
//4、获取公网IP对象
InetAddress address2 = InetAddress.getByName("110.242.68.3");
System.out.println(address2.getHostName());
System.out.println(address2.getHostAddress());
//5、判断是否能通:ping 5s之前测试是否可通
System.out.println(address2.isReachable(5000)); //返回true
}
}
端口:
端口号:范围0~65535
端口号的作用
- 唯一标识正在计算机设备上运行的进程(程序)
一个设备中,能否出现2个应用程序的端口号一样,为什么?
- 不可以,如果一样会出现端口冲突错误
传输层的2个常见协议
- TCP:传输控制协议
- UDP:用户数据报协议
TCP协议特点
- 使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议
- 传输前,采用“三次握手”方式建立连接,所以是可靠的
- 在连接中可进行大数据量的传输
- 连接,发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低
TCP协议通信场景
- 对信息安全要求较高的场景,例如:文件下载、金融等数据通信
三次握手
客户端 ——> 服务器:客户端向服务器发出连接请求,等待服务器确认
服务器 ——> 客户端:服务器向客户端返回一个响应,告诉客户端收到了请求
客户端 ——> 服务器:客户端向服务器再次发出确认信息,连接建立
TCP四次挥手断开连接
客户端 ——> 服务器:客户端向服务器发出取消连接请求
…………..
UDP协议
- UDP是一种无连接、不可靠传输的协议
- 语音通话,视频会话等。
UDP
DatagramPacket:数据包对象(韭菜盘子)
- 构造器:public DatagramPacket(byte[] buf, int length, InetAddress address, int port);
- 创建发送端数据包对象;
- buf:要发送的内容,字节数组;
- length:要发送内容的字节长度;
- address:接收端的IP地址对象;
- port:接收端口号
DatagramSocket:发送端和接收端对象(人)
- 构造器:public DatagramSocket();创建发送端的Socket对象,系统会随机分配一个端口号
- 构造器2:public DatagramSocket(int port):创建接收端的Socket对象并指定端口号
- send(DatagramPacket dp);发送数据包
- receive(DatagramPacket p);接收数据包
一发一收
发送端
package com.h.UDP;
import java.net.*;
import java.nio.charset.StandardCharsets;
/*
* 发送端
* */
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
//1、创建发送端对象:发送端自带默认端口号。不填7777就是默认端口填7777是指定端口
DatagramSocket socket = new DatagramSocket(7777);
//2、创建一个数据包对象封装数据(韭菜盘子)
//参数
//参数一:封装要发送的数据(韭菜) byte[]
//参数二:数据大小 int length
//参数三:服务端IP地址 对方的ip
//参数四:服务端的端口 对方的端口
byte[] buff = "我是韭菜".getBytes();
//这个8888端口是将数据包发送给服务端的socket端口必须一样
DatagramPacket packet = new DatagramPacket(buff,4,
InetAddress.getLocalHost() ,8888);
//3、发送数据出去
socket.send(packet);
socket.close();
}
}
接收端
package com.h.UDP;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/*
* 接收端 服务端
* */
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("===服务端启动===");
//1、创建接收端对象:注册端口(人) 这里的Socket8888端口必须和数据包里面的端口一致
DatagramSocket socket = new DatagramSocket(8888);
//2、创建一个数据包对象接数据
byte[] buff = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buff,buff.length);
//3、接收数据
socket.receive(packet);
//4、取出数据
//读多少倒多少
int len = packet.getLength();
String str = new String(buff,0,len);
//获取发送端的ip
String address = packet.getSocketAddress().toString();
//这个端口是对方默认端口
int port = packet.getPort();
System.out.println(str+" "+port+" "+address);
socket.close();
}
}
广播:
当前主机与所在网络中所有主机通信,广播消息是可以发给当前所在网络中的所有服务器
广播ip:255.255.255.255
package com.h.udp;
import java.net.*;
/*
* 发送端
* */
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
//1、创建发送端对象:发送端自带默认端口号。
DatagramSocket socket = new DatagramSocket();
//2、创建一个数据包对象封装数据(韭菜盘子)
//参数
//参数一:封装要发送的数据(韭菜) byte[]
//参数二:数据大小 int length
//参数三:服务端IP地址 对方的ip
//参数四:服务端的端口 对方的端口
byte[] buff = "我是韭菜".getBytes();
//这个8888端口是将数据包发送给服务端的socket端口必须一样
//广播IP 255.255.255.255
DatagramPacket packet = new DatagramPacket(buff,4,
InetAddress.getByName("255.255.255.255") ,8888);
//3、发送数据出去
socket.send(packet);
socket.close();
}
}
//接收端
package com.h.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
* 接收端 服务端
* */
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("===服务端启动===");
//1、创建接收端对象:注册端口(人) 这里的Socket8888端口必须和数据包里面的端口一致
DatagramSocket socket = new DatagramSocket(8888);
//2、创建一个数据包对象接数据
byte[] buff = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buff,buff.length);
//3、接收数据
socket.receive(packet);
//4、取出数据
//读多少倒多少
int len = packet.getLength();
String str = new String(buff,0,len);
//获取发送端的ip
String address = packet.getSocketAddress().toString();
//这个端口是对方默认端口
int port = packet.getPort();
System.out.println(str+" "+port+" "+address);
socket.close();
}
}
组播:
- 使用组播地址:224.0.0.0 ~ 239.255.255.255
- 具体操作
- 发送端的数据包的目的地是组播IP(例如:224.0.1.1,端口9999)
- 接收端必须绑定该组播IP(224.0.1.1),端口要对应发送端的端口9999,这样即可接收消息
- DatagramSocket的子类MulticastSocket可以在接收端绑定组播IP
组播是可以选择性的发送给指定ip的服务器
//发送端
package com.h.udp_multicast;
import java.net.*;
public class Cline {
public static void main(String[] args) {
try {
//创建发送器
DatagramSocket d = new DatagramSocket();
byte[] buffer = "组播".getBytes();
//创建数据包
//组播ip为224.0.0.0 ~ 239.255.255.255
DatagramPacket dp = new DatagramPacket(buffer,buffer.length, InetAddress.getByName("224.0.1.1"),8888);
d.send(dp);
d.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//接收端
package com.h.udp_multicast;
import java.net.*;
/*
* 接收端 服务端
* */
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("===服务端启动===");
//组播主要是需要MulticastSocket这个对象
//创建接收端对象 注册端口
MulticastSocket socket = new MulticastSocket(8888);
//当前接收端加入到一个组播组里面去:绑定对应的组播ip
// socket.joinGroup(InetAddress.getByName("224.0.1.1")); 过时的写法
//使用新的写法
socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1"),8888),
NetworkInterface.getByInetAddress(InetAddress.getLocalHost()));
//2、创建一个数据包对象接数据
byte[] buff = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buff,buff.length);
//3、接收数据
socket.receive(packet);
//4、取出数据
//读多少倒多少
int len = packet.getLength();
String str = new String(buff,0,len);
//获取发送端的ip
String address = packet.getSocketAddress().toString();
//这个端口是对方默认端口
int port = packet.getPort();
System.out.println(str+" "+port+" "+address);
socket.close();
}
}
多发多收
package com.h.udp2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/*
* 发送端 多发多收
* */
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
//1、创建发送端对象:发送端自带默认端口号。
DatagramSocket socket = new DatagramSocket();
//2、创建一个数据包对象封装数据(韭菜盘子)
//参数
//参数一:封装要发送的数据(韭菜) byte[]
//参数二:数据大小 int length
//参数三:服务端IP地址 对方的ip
//参数四:服务端的端口 对方的端口
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String s = sc.nextLine();
if(s.equals("exit")){
System.out.println("离线成功");
socket.close();
break;
}
byte[] buff = s.getBytes();
//这个8888端口是将数据包发送给服务端的socket端口必须一样
DatagramPacket packet = new DatagramPacket(buff, buff.length,
InetAddress.getLocalHost(), 8888);
//3、发送数据出去
socket.send(packet);
}
}
}
接收端
package com.h.udp2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;
/*
* 接收端 服务端 多发多收
* */
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("===服务端启动===");
//1、创建接收端对象:注册端口(人) 这里的Socket8888端口必须和数据包里面的端口一致
DatagramSocket socket = new DatagramSocket(8888);
byte[] buff = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buff, buff.length);
while (true) {
socket.receive(packet);
//收到多收数据
int len = packet.getLength();
System.out.println("收到了来自:"+packet.getPort()+
packet.getSocketAddress()+"的数据"+
new String(buff,0,len));
}
}
}
基础版互相聊天:使用线程来来完成
package com.h.udp2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;
/*
* 发送端 多发多收
* */
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
//1、创建发送端对象:发送端自带默认端口号。
//2、创建一个数据包对象封装数据(韭菜盘子)
//参数
//参数一:封装要发送的数据(韭菜) byte[]
//参数二:数据大小 int length
//参数三:服务端IP地址 对方的ip
//参数四:服务端的端口 对方的端口
new Thread() {
@Override
public void run() {
Scanner sc = new Scanner(System.in);
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
while (true) {
System.out.println("请说:");
String s = sc.nextLine();
if (s.equals("exit")) {
System.out.println("离线成功");
socket.close();
break;
}
byte[] buff = s.getBytes();
//这个8888端口是将数据包发送给服务端的socket端口必须一样
DatagramPacket packet = new DatagramPacket(buff, buff.length,
InetAddress.getLocalHost(), 7777);
//3、发送数据出去
socket.send(packet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
new Thread(){
@Override
public void run() {
System.out.println("===服务端启动===");
//1、创建接收端对象:注册端口(人) 这里的Socket8888端口必须和数据包里面的端口一致
DatagramSocket socket = null;
try {
socket = new DatagramSocket(8888);
byte[] buff = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buff, buff.length);
while (true) {
//等待接收的数据
socket.receive(packet);
//收到多收数据
int len = packet.getLength();
System.out.println("收到了来自:"+packet.getPort()+
packet.getSocketAddress()+"的数据"+
new String(buff,0,len));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}
第二个端
package com.h.udp2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;
/*
* 接收端 服务端 多发多收
* */
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
new Thread() {
@Override
public void run() {
Scanner sc = new Scanner(System.in);
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
while (true) {
System.out.println("请说:");
String s = sc.nextLine();
if (s.equals("exit")) {
System.out.println("离线成功");
socket.close();
break;
}
byte[] buff = s.getBytes();
//这个8888端口是将数据包发送给服务端的socket端口必须一样
DatagramPacket packet = new DatagramPacket(buff, buff.length,
InetAddress.getLocalHost(), 8888);
//3、发送数据出去
socket.send(packet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
new Thread(){
@Override
public void run() {
System.out.println("===服务端启动===");
//1、创建接收端对象:注册端口(人) 这里的Socket8888端口必须和数据包里面的端口一致
DatagramSocket socket = null;
try {
socket = new DatagramSocket(7777);
byte[] buff = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buff, buff.length);
while (true) {
//等待接收的数据
socket.receive(packet);
//收到多收数据
int len = packet.getLength();
System.out.println("收到了来自:"+packet.getPort()+
packet.getSocketAddress()+"的数据"+
new String(buff,0,len));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}
UDP广播,组播
TCP:
三次握手,
客户端和服务端通信之间,双方会建立一个连接管道,Socket
TCP是通过流来进行实现消息的传递
Socket :
构造器:public Socket(String host,int port);创建一个发送端的Socket对象与服务端连接,参数为服务端程序的ip地址和端口
成员方法:
- 创建客户端的Socket对象,请求与服务端的连接
- 使用socket对象调用getOutputStream()方法得到一个字节输出流
- 使用字节输出流完成数据的发送
- 释放资源:关闭资源
现在服务端为什么不可以接收多个客户端
- TCP服务端是单线程一次只能处理一个客户端所以不能实现一个服务端接收多个客户端
TCP代码实现
一发一收:
//服务端
package com.h.tcp;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 服务端
* */
public class TcpServer {
public static void main(String[] args) {
System.out.println("服务端启动");
try (
ServerSocket ss = new ServerSocket(7777);
) {
//阻塞等待客户端连接
Socket socket = ss.accept();
//如果阻塞在IO后面的话就会出现编译错误,因为阻塞是在等客户端的流
InputStream in = socket.getInputStream();
//使用高级字符流来获取文本信息 使用了转换流
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String str ;
//对方挂了server也挂 实现了一发一收
if ((str = br.readLine())!=null) {
System.out.println(socket.getRemoteSocketAddress()+"说了:"+str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//接收端
package com.h.tcp;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/*
* 客户端
* */
public class Tcpclient {
public static void main(String[] args) {
try{
//参数一、服务端ip地址
//参数二、服务端端口号
// 10.50.128.211
Socket socket = new Socket("10.50.128.211",7777);
//从socket中得到一个字节输出流,负责发送消息
//把低级的字节流包装成打印流
PrintStream ps = new PrintStream(out);
// //发送消息
ps.println("你好TCP服务器,我是客户端");
ps.flush();
//关闭资源
socket.close();
}catch (Exception e){
}
}
}
TCP多发多收
package com.h.tcp2;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 服务端
* */
public class TcpServer {
public static void main(String[] args) {
System.out.println("服务端启动");
try (
ServerSocket ss = new ServerSocket(7777);
) {
//阻塞等待客户端连接
Socket socket = ss.accept();
//如果阻塞在IO后面的话就会出现编译错误,因为阻塞是在等客户端的流
InputStream in = socket.getInputStream();
//使用高级字符流来获取文本信息 使用了转换流
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String str;
//每次接收到了客户端后就会一直在这等消息
while ((str = br.readLine()) != null) {
System.out.println(socket.getRemoteSocketAddress() + "说了:" + str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.h.tcp2;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/*
多发多收
* 客户端
* */
public class Tcpclient {
public static void main(String[] args) {
try {
//创建用户输入类
Scanner sc = new Scanner(System.in);
//参数一、服务端ip地址
//参数二、服务端端口号
// 10.50.128.211
Socket socket = new Socket("10.50.128.214", 7777);
//从socket中得到一个字节输出流,负责发送消息
OutputStream out = socket.getOutputStream();
//把低级的字节流包装成打印流 这里只是把低级的流进行了包装后再发过去
PrintStream ps = new PrintStream(out);
while (true) {
//发送消息
System.out.println("请输入你要发送的内容");
String s = sc.nextLine();
//发送内容
ps.println(s);
ps.flush();
}
} catch (Exception e) {
}
}
}
完成一个服务端同时处理多个客户端消息
- 使用线程实现了一个服务端接收多个客户端
- Socket.accept()这里阻塞直到获取一个客户端后代码才会继续向下执行
- 获取到了客户端后接下来就会在read()方法哪里等待可以客户端输入内容过来,会一直在获取客户端内容
- 所以用while来创建多个来接收多个客户端的,一个线程处理一个客户端
- 这样一来就实现了一个服务端接收多个客户端信息
代码:
//服务端
System.out.println("=====服务端启动=====");
try {
ServerSocket ss = new ServerSocket(7777);
while (true) {
//每次都会接收到客户端电的Socket管道,交给一个独立线程,会在这里阻塞
Socket socket = ss.accept();
//开始创建独立线程处理socket
new ThreadT(socket).start();
}
//线程
public class ThreadT extends Thread{
private Socket socket;
public ThreadT(Socket socket){
this.socket = socket;
}
public void run() {
try {
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String str ;
while ((str = br.readLine())!=null){
System.out.println(socket.getRemoteSocketAddress()+"说:"+str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//客户端
package com.h.tcp2;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/*
多发多收
* 客户端
* */
public class Tcpclient {
public static void main(String[] args) {
try {
//创建用户输入类
Scanner sc = new Scanner(System.in);
//参数一、服务端ip地址
//参数二、服务端端口号
// 10.50.128.211
Socket socket = new Socket("10.50.128.214", 7777);
//从socket中得到一个字节输出流,负责发送消息
OutputStream out = socket.getOutputStream();
//把低级的字节流包装成打印流 这里只是把低级的流进行了包装后再发过去
PrintStream ps = new PrintStream(out);
while (true) {
//发送消息
System.out.println("请输入你要发送的内容");
String s = sc.nextLine();
if (s.equals("exit")) {
break;
}
//发送内容
ps.println(s);
ps.flush();
}
} catch (Exception e) {
}
}
}
使用线程池实现:
- 服务端可以复用线程处理多个客户端,可以避免系统瘫痪 ```java //客户端 package com.h.tcp_many_majorization;
import com.h.tcp_many.ThreadT;
import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.*;
/
- 使用线程池进行优化
*/ public class TcpServerDuo优化 { //使用静态变量记住一个线程池对象 private static ExecutorService tpe = new ThreadPoolExecutor(3, 5, 6,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
System.out.println("=====服务端启动=====");
try {
//注册端口
ServerSocket ss = new ServerSocket(7777);
while (true) {
//每次都会接收到客户端电的Socket管道,交给一个独立线程,会在这里阻塞
Socket socket = ss.accept();
System.out.println(socket.getRemoteSocketAddress() + "上线了");
//创建线程类
ServerRunnable s = new ServerRunnable(socket);
tpe.execute(s);
}
} catch (Exception e) {
e.printStackTrace();
}
} } //线程 package com.h.tcp_many_majorization;
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket;
public class ServerRunnable implements Runnable{
private Socket socket;
public ServerRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String str ;
while ((str = br.readLine())!=null){
System.out.println(socket.getRemoteSocketAddress()+"说:"+str);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"这个吊毛已经离线了");
}
}
} //接收端跟上面一样
<a name="QpktB"></a>
### TCP即时通信
即时通信是什么含义,要实现怎么样的设计
- 发送消息个服务端,服务端在转发给所有客户端
- 即时通信,是指一个客户端的消息发出去,其他客户端可以接收到
- 即时通信需要进行端口转发的设计思想
- 服务端需要把在线的Socket管道存储起来
- 一旦收到一个消息要推送给其他管道
代码实现:
```java
package com.h.tcpim;
import com.h.tcp_many_majorization.ServerRunnable;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/*
*
* 端口转发 即时通信
* */
public class TcpServerIM {
//定义一个静态的List集合存储当前全部在线的Socket管道
public static List<Socket> list = new ArrayList<>();
//使用静态变量记住一个线程池对象
private static ExecutorService tpe = new ThreadPoolExecutor(3, 5, 6,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
System.out.println("=====服务端启动=====");
try {
//注册端口
ServerSocket ss = new ServerSocket(7777);
while (true) {
//每次都会接收到客户端电的Socket管道,交给一个独立线程,会在这里阻塞
Socket socket = ss.accept();
//每次有一个客户端上线就把他加入到这个集合中去
list.add(socket);
System.out.println(socket.getRemoteSocketAddress() + "上线了");
//创建线程类
ThreadIM s = new ThreadIM(socket);
tpe.execute(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ThreadIM extends Thread{
private Socket socket;
public ThreadIM(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String str ;
while ((str = br.readLine())!=null){
System.out.println(socket.getRemoteSocketAddress()+"说:"+str);
//服务端收到消息后 转发出去 进行端口转发发送给全部的Socket管道
sendMsgToAll(str);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"这个吊毛已经离线了");
//当客户端挂掉后 就会从集合中删除这个socket 这样程序就不会报错
TcpServerIM.list.remove(socket);
}
}
private void sendMsgToAll(String str) throws Exception {
//list里面有所有的上线了的客户端,所以我们就把这些客户端遍历出来把消息通过流在发出去
for (Socket socket: TcpServerIM.list ) {
PrintStream pr = new PrintStream(socket.getOutputStream());
pr.println(str);
pr.flush();
}
}
}
package com.h.tcpim;
import java.io.*;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
/*
多发多收
* 客户端
* */
public class TcpClient {
public static void main(String[] args) throws Exception {
//创建用户输入类
Scanner sc = new Scanner(System.in);
//参数一、服务端ip地址
//参数二、服务端端口号
// 10.50.128.211
Socket socket = new Socket("10.50.128.214", 7777);
//创建一个独立的线程专门负责这个客户端的读消息,服务器随时会转发消息过来
new ClientReaderThread(socket).start();
//从socket中得到一个字节输出流,负责发送消息
OutputStream out = socket.getOutputStream();
//把低级的字节流包装成打印流 这里只是把低级的流进行了包装后再发过去
PrintStream ps = new PrintStream(out);
while (true) {
//发送消息
System.out.println("请输入你要发送的内容");
String s = sc.nextLine();
if (s.equals("exit")) {
break;
}
//发送内容
ps.println(s);
ps.flush();
}
}
}
class ClientReaderThread extends Thread {
private Socket socket;
public ClientReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String str;
while ((str = br.readLine()) != null) {
System.out.println(getName() + "发送消息:" + str);
}
} catch (Exception e) {
System.out.println("服务端把你踢出去了");
}
}
}
总结
UDP使用的是DatagramSocket类DatagramPacket
客户端
- DatagramSocket socket = new DatagramSocket();
- DatagramPacket packet = new DatagramPacket(buff, buff.length, InetAddress.getLocalHost(), 7777);
- 使用socket.send();
服务端
- DatagramSocket socket = new DatagramSocket(7777);//注册端口
- DatagramPacket packet = new DatagramPacket(buff,buff.length);
- socket.receive();
TCP使用
客户端
- Socket socket = new Socket(“10.50.128.214”, 7777);
- socket.getOutputStream();
服务端
- ServerSocket ss = new Socket(777);//注册端口
- ss.accept();
- ss.getInputStream();
使用代码下载音乐
package com.hhm.下载音乐;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class 下载音乐 {
public static void main(String[] args) {
try {
//下载地址
URL url = new URL("https://m801.music.126.net/20211214174656/0047165544898dca518d32057e866e50/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/11858827254/839c/479d/c797/5b2e139670c187c5391ef2e22d698d2d.m4a");
//连接到这个资源
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
//下载下来的名字自己改
FileOutputStream fos = new FileOutputStream("f.m4a");
byte[] bytes = new byte[1024];
int len ;
while ((len = inputStream.read(bytes)) != -1){
fos.write(bytes,0,len);//写出这个数据
}
fos.close();
inputStream.close();
urlConnection.disconnect();//断开连接
} catch (Exception e) {
e.printStackTrace();
}
}
}