通信数据源有哪些: 文件、byteArrayOutputStream、socke管道、控制台
1. 基础概念:
1. 地址:Ip地址
2. 端口: 计算机中区分不同进程
同一个协议下,端口不能重复使用,不同协议可以
1024以下端口预留给系统的 比如http 80 ftp 21
URL: 统一资源定位符
组成:
协议+主机域名+端口+资源文件名
3. 数据传输协议
tcp 协议: 三次握手,面向连接,数据流,打电话,数据不会丢失
udp 协议, 发短信, 数据可能都是
协议分成网络通信封装:
1. 数据封装,发送数据
应用层: http,ftp,snmp 都是应用层协议
传输层: 端口, tcp/udp 传输层协议,具体数据包结构百度
网络层: Ip,具体数据包结构百度
数据连接层: Mac地址
2. 数据拆分: 数据接收
2. 地址 Api 基础 使用
InetAddress 封装IP地址和域名
InetSocketAddress 封装IP和端口
URL 统一资源定位符
组成: 协议+主机域名+端口+资源文件名
通过 URL 获取资源,网络爬虫
2.1.InetAddress 封装IP地址和域名
public static void main(String[] args) throws URISyntaxException, IOException {
/**
* InetAddress 封装IP地址和域名
*/
InetAddress addr= InetAddress.getLocalHost();
System.out.println(addr.getHostAddress()); // 获取本机Ip,192.168.2.208
System.out.println(addr.getHostName()); // 获取计算机名
addr= InetAddress.getByName("www.baidu.com");
System.out.println(addr.getHostAddress()); //根据域名返回Ip
addr= InetAddress.getByName("182.61.200.6");
System.out.println(addr.getHostAddress());
System.out.println(addr.getHostName()); // 如果Ip地址不存在,或者dns不解析,返回Ip,否则返回域名
}
2.2.InetSocketAddress 封装IP和端口
public static void main(String[] args) throws URISyntaxException, IOException {
/**
* 封装IP和端口
*/
// InetSocketAddress addr1=new InetSocketAddress("127.0.0.1",8888);
// System.out.println(addr1.getAddress()); //
// System.out.println(addr1.getHostName());
// System.out.println(addr1.getPort());
}
2.3. URL 统一资源定位符
组成: 协议+主机域名+端口+资源文件名
public static void main(String[] args) throws URISyntaxException, IOException {
/**
* URL: 统一资源定位符
组成: 协议+主机域名+端口+资源文件名
*/
// URL url=new URL("http://www.baidu.com:8080/myServlet?username=xiaoming");
// System.out.println("协议:"+url.getProtocol());
// System.out.println("主机名:"+url.getHost());
// System.out.println("端口:"+url.getPort());
// System.out.println("资源:"+url.getFile());
// System.out.println("相对路径:"+url.getPath());
// System.out.println("锚点:"+url.getRef());
// System.out.println("参数:"+ url.getQuery());
/**
* 协议:http
主机名:www.baidu.com
端口:8080
资源:/myServlet?username=xiaoming
相对路径:/myServlet
锚点:null
参数:username=xiaoming
*/
// 类似File相对路径
URL url1= new URL("http://baidu.com:8080/a/");
URL url2= new URL(url1, "b.txt");
System.out.println("url2:"+url2.toString()); //http://baidu.com:8080/a/b.txt
}
2.4. 通过 URL 获取资源,网络爬虫
public static void main(String[] args) throws URISyntaxException, IOException {
/**
* 资源访问 通过流, 模仿网络爬虫, 爬虫网页 保持 到本地
*/
URL url3=new URL("http://www.baidu.com");
InputStream inputStream=url3.openStream();
BufferedReader reader=new BufferedReader(
new InputStreamReader(inputStream,"utf-8"));
String msg=null;
BufferedWriter bw=new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("baidu.html"),"utf-8"));
while(( msg=reader.readLine())!=null) {
bw.append(msg);
bw.newLine();
System.out.println(msg);
}
bw.flush();
bw.close();
reader.close();
}
3. Udp通信
3.1. 发送字符串 UdpServer 端:
3.2.发送基本数据类型,比如子弹个数发送
【把double转化为内存中double,不是字符串, Double在内存中存储
字符串在内存中是逐个字符的ASCII码存储的】
package com.denganzhi.pp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UdpServer {
public static void main0001(String[] args) throws IOException {
// 1. 创建 服务端 Ip+ 端口
DatagramSocket server=new DatagramSocket(8888,InetAddress.getByName("localhost"));
// 2. 准备接受数据容器
byte[] container=new byte[1024];
// 3. 封装包
DatagramPacket packet=new DatagramPacket(container,container.length);
// 4. 接收数据
server.receive(packet);
// 5.分析数据
byte[] data=packet.getData();
int len= packet.getLength();
System.out.println(new String(data,0,len));
// 6. 释放
server.close();
}
public static void main(String[] args) throws IOException {
// 1. 创建 服务端 Ip+ 端口
DatagramSocket server=new DatagramSocket(8888,InetAddress.getByName("localhost"));
// 2. 准备接受数据容器
byte[] container=new byte[1024];
// 3. 封装包
DatagramPacket packet=new DatagramPacket(container,container.length);
// 4. 接收数据, 阻塞
System.out.println("udp阻塞");
server.receive(packet);
System.out.println("udp接收数据");
// 5.分析数据
Double result= convert(packet.getData());
System.out.println("result:"+result);
// 6. 释放
server.close();
}
// data-> double
public static double convert(byte[] data) throws IOException {
DataInputStream dis=new DataInputStream(new ByteArrayInputStream(data));
double num= dis.readDouble();
dis.close();
return num;
}
}
UdpClient 端:
package com.denganzhi.pp;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UdpClient {
/**
* 字符串 发送
* @param args
* @throws IOException
*/
public static void main001(String[] args) throws IOException {
// 1. 创建 客户端+ 端口
DatagramSocket client=new DatagramSocket(6666,InetAddress.getByName("localhost"));
// 2. 数据准备
String msg="udp编程";
byte[] data= msg.getBytes();
//3. 打包( 发送地点以及 端口), 打包直接发送,所有udp有丢包可能
DatagramPacket packet=new DatagramPacket(data, data.length,new InetSocketAddress("127.0.0.1",8888));
// 4. 发送数据
client.send(packet);
// 5. 关闭资源
client.close();
}
/**
* 基本数据类型 的传输
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 1. 创建 客户端+ 端口
DatagramSocket client=new DatagramSocket(6666,InetAddress.getByName("localhost"));
// 2. 数据准备
byte[] data= convert(11.01);
//3. 打包( 发送地点以及 端口)
DatagramPacket packet=new DatagramPacket(data, data.length,new InetSocketAddress("127.0.0.1",8888));
// 4. 发送数据
client.send(packet);
// 5. 关闭资源
client.close();
}
public static byte[] convert(double num) throws IOException {
byte[] data=null;
ByteArrayOutputStream bos=new ByteArrayOutputStream();
DataOutputStream dos=new DataOutputStream(bos);
dos.writeDouble(num);
dos.flush();
data= bos.toByteArray();
dos.close();
return data;
}
}
4. Tcp通信
4.1. TcpServer 发送字符串、发送基本数据类型,直接写DataInputStream,服务端
package com.denganzhi.pp;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
//1.. 创建服务器, 指定端口
ServerSocket server=new ServerSocket(8888);
// 2. 指定客户端 阻塞式
// 完成 三次握手 , 建立管道
System.out.println("服务器阻塞,等待客户端连接");
Socket socket= server.accept();
System.out.println("一个客户端建立连接");
// 服务端输出数据
// BufferedWriter bw=new BufferedWriter(
// new OutputStreamWriter(socket.getOutputStream()));
// // 必须 有行 结束符,socket内存数据才可以被写出
// String str="我是来自服务器";
// bw.write(str);
// bw.newLine(); // 必须有
// bw.flush();
// 直接写出基本数据类型,比如游戏中子弹个数
DataOutputStream dos=new DataOutputStream(
socket.getOutputStream());
dos.writeDouble(11.01);
dos.flush();
}
}
客户端实现:
package com.denganzhi.pp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class TcpClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1. 创建客户端 必须指定要连接的 服务器
// tcp 客户端端口有系统分配
Socket clinet=new Socket("localhost",8888);
//BufferedReader reader=new BufferedReader(
// new InputStreamReader(
// clinet.getInputStream()));
//
//String echo= reader.readLine();
//
//System.out.println(echo);
DataInputStream dis=new DataInputStream(clinet.getInputStream());
double result = dis.readDouble();
System.out.println("result:"+result);
}
}
5. Tcp实现聊天实现
要点: 1. 客户端可以发送数据, 必须要把收发数据放入不同的线程中
2. 服务器实现多个连接客户端监听
服务器必须受到数据才发送数据到客户端
所以服务器必须把不同客户端放入独立的线程中
代码流程:
1.
Client: 1. 实现客户端上线功能, 为每一个客户端接发数据启动一个线程
Server: 1. 这里实现客户端上线功能,为每一个客户端通道启动一个线程, 把任务我放入List中
2.
Client : 客户端A发送数据,
Server: 服务器A通道线程接收数据,服务器sendOthers() 发送给List中其他任务线程,如果是群聊,遍历List发送即可,
如果是私聊,在List找到对应 线程通道根据name ,send即可
实现功能: 群聊、 私聊 @test:您好
版本1:服务器代码支持客户A发送到服务器,服务器回客户端A,服务器只是支持一个客户端
package com.denganzhi.multi;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
//1.. 创建服务器, 指定端口
ServerSocket server=new ServerSocket(8888);
// 2. 指定客户端 阻塞式
// 完成 三次握手 , 建立管道
System.out.println("服务器阻塞,等待客户端连接");
// while(true) { // 服务器实现多线程,多个客户端连接
Socket socket= server.accept();
System.out.println("一个客户端建立连接");
DataInputStream dis=new DataInputStream(socket.getInputStream());
DataOutputStream dos=new DataOutputStream(
socket.getOutputStream());
while(true) {
// 读取客户端数据
String msg= dis.readUTF();
System.err.println("服务收到数据:"+msg);
// 服务器输出数据
dos.writeUTF("服务器输出数据120");
dos.flush();
}
// }
}
}
版本2:服务器代码支持群聊,私聊版本:
package com.denganzhi.multi;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class TcpServer {
List<MyChannel> allChannels=new ArrayList<>();
public static void main(String[] args) throws IOException {
new TcpServer().start();
}
public void start() throws IOException {
//1.. 创建服务器, 指定端口
ServerSocket server=new ServerSocket(8888);
// 2. 指定客户端 阻塞式
// 完成 三次握手 , 建立管道
System.out.println("服务器阻塞,等待客户端连接");
while(true) { // 服务器实现多线程,多个客户端连接
Socket socket= server.accept();
System.out.println("一个客户端建立连接");
// 服务器接发数据放入一个线程中
MyChannel myChannel=new MyChannel(socket);
// 把通过的客户端连接任务装起来
allChannels.add(myChannel);
new Thread(myChannel).start();
}
}
class MyChannel implements Runnable{
boolean isRunning=true;
DataInputStream dis=null;
DataOutputStream dos=null;
private String name;
public MyChannel(Socket clinet) {
try {
dis = new DataInputStream(clinet.getInputStream());
dos=new DataOutputStream(
clinet.getOutputStream());
String name= dis.readUTF();
this.name= name;
// 1. 这里实现客户端上线功能,为每一个Runnable分配一个name
System.out.println(name+"进入聊天室");
this.send("欢迎自己"+this.name+ "欢迎进入聊天室");
this.sendOthers("通知"+this.name+" 进入聊天室");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
CloseUtils.closeAll(dis,dos);
isRunning=false;
allChannels.remove(this);
}
}
@Override
public void run() {
while(isRunning) {
// 读取客户端数据 ,实现私聊功能
// String msg=receive();
// send(msg);
// 实现群聊
sendOthers();
}
}
public String receive() {
String msg="";
try {
msg = dis.readUTF();
System.err.println(this.name+"发送过来数据-----》"+ "服务收到数据:"+msg);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
CloseUtils.closeAll(dis,dos);
isRunning=false;
allChannels.remove(this);
}
return msg;
}
// 发送数据
public void send(String msg) {
// 服务器输出数据
try {
/// dos.writeUTF("服务器输出数据120");
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
CloseUtils.closeAll(dis,dos);
isRunning=false;
allChannels.remove(this);
}
}
public void sendOthers() {
String msg= receive();
// @test:您好
// 判断私聊还是群聊
if(msg.startsWith("@")) {
// 在容器中知道对应的人
String name= msg.substring(1,msg.indexOf(":"));
// substring 左 关闭右 开
String content= msg.substring(msg.indexOf(":")+1);
for (MyChannel myChannel : allChannels) {
if(myChannel.name.equals(name)) {
myChannel.send(this.name+"私聊 你: " + content+ "");
}
}
}else {
// 发送给所有人
for (MyChannel myChannel : allChannels) {
if(myChannel==this) {
continue;
}
// 发送给其他客户客户端
myChannel.send("群发消息:"+ msg);
}
}
}
public void sendOthers(String msg) {
for (MyChannel myChannel : allChannels) {
if(myChannel==this) {
continue;
}
// 发送给其他客户客户端
myChannel.send(msg);
}
}
}
}
客户端代码:
package com.denganzhi.multi;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import javax.swing.plaf.ColorChooserUI;
public class TcpClient {
static boolean cIsRunning=true;
static boolean sIsRunning=true;
public static void main(String[] args) throws UnknownHostException, IOException {
//1. 创建客户端 必须指定要连接的 服务器
// tcp 客户端端口有系统分配
Socket clinet=new Socket("localhost",8888);
// 客户端 写数据
// 2. 客户端接收、发送消息位于不同线程中
new Thread(new Runnable() {
boolean isFirst=true;
@Override
public void run() {
while(cIsRunning) {
DataOutputStream dos=null;
if(isFirst) {
// 1. 客户端上线功能
System.out.println("请输入用户名:");
}else{
System.out.println("请输入用发送信息:");
}
isFirst=false;
Scanner sc=new Scanner(System.in);
String sendMsg= sc.next();
//BufferedReader sc=new BufferedReader(new InputStreamReader(System.in));
try {
// String sendMsg= sc.readLine();
if(sendMsg==null && sendMsg!="" && sendMsg.trim()!="") {
return ;
}
dos = new DataOutputStream(clinet.getOutputStream());
// dos.writeUTF("客户端写入服务器数据110");
dos.writeUTF(sendMsg);
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
cIsRunning= false; // 出异常了,停止发送数据
CloseUtils.closeAll(dos);
}
}
}
}).start();
// 2. 客户端接收、发送消息位于不同线程中
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(sIsRunning) {
DataInputStream dis=null;
try {
dis = new DataInputStream(clinet.getInputStream());
String result = dis.readUTF();
System.err.println("服务端回的数据:"+result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
sIsRunning=false;
CloseUtils.closeAll(dis);
}
}
}
}).start();
}
}
CloseUtils 工具类:
package com.denganzhi.multi;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.IOException;
public class CloseUtils {
// 可变参数 ,工具类关闭流
// 规则: 如果有其他参数,只能在最后一个位置,处理方式和数组一致
//
public static void closeAll(Closeable ... io){
for (Closeable closeable : io) {
if(closeable!=null){
try {
closeable.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
Server服务端终端控制台输出:
ClientA客户端控制台输出:
ClientB客户端控制台输出:
源码地址:https://download.csdn.net/download/dreams_deng/12305064