通信数据源有哪些: 文件、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(); } // >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