网络编程:TCP和UDP
传输协议
1、TCP:传输控制协议
2、UDP:用户数据报协议
TCP
面向连接的、安全可靠的,三次握手,性能较低
UDP
不建立连接、不可靠的、性能高、易丢包
Socket套接字:应用层与传输层的接口
UDP编程
概述
通信双方不需要建立连接
通信双方完全平等
QQ聊天模式
数据以包裹为中心:封包和拆包
方法
1、DatagramSocket:用于发送或者接受数据包的套接字
2、DatagramPacket:数据包:封包和拆包
使用对象
客户端:Client
接收端:Server
编写流程:Client端
每个进程具有一个或多个套接字,所以在目的主机指定特定的套接字是必要的。当生成一个套接字时,就为它分配一个端口号
1、使用DatagramSocket 指定端口创建发送端
2、准备数据转成字节数组
3、封装成DatagramPacket包裹,需要指定目的地(IP+端口)
4、发送包裹send(DatagramPacket p)
5、释放资源
代码片段
/*** 发送端:Client* 1、使用DatagramSocket 指定端口创建发送端* 2、准备数据转成字节数组* 3、封装成DatagramPacket包裹,需要指定目的地(IP+端口)* 4、发送包裹send(DatagramPacket p)* 5、释放资源*/public class UdpClient {public static void main(String[] args) throws Exception {System.out.println("发送方启动中......");//1、使用DatagramSocket对象 指定端口创建发送端DatagramSocket client=new DatagramSocket(8888);//准备数据转成字节数组String data="山东扒鸡,买一送一!";byte[]datas=data.getBytes();//3、封装成DatagramPacket包裹,需要指定目的地DatagramPacket packet=new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",9999));//4、发送包裹send(DatagramPacket p)client.send(packet);//5、释放资源client.close();}}
编写流程:Server端
1、使用 DatagramSocket 指定端口创建接收端
2、准备容器封装成 DatagramPacket 包裹
3、阻塞式接受包裹 receive(DatagramPacket p)
4、分析数据 byte[]getData() ; getLength()
5、释放资源
代码片段
/*** @auther TongFangPing* @date 2019/10/7 11:26.* 接收端* 1、使用DatagramSocket 指定端口创建接收端* 2、准备容器封装成DatagramPacket包裹* 3、阻塞式接受包裹receive(DatagramPacket p)* 4、分析数据* byte[]getData()* getLength()* 5、释放资源*/public class UdpServer {public static void main(String[] args) throws Exception {System.out.println("接收方启动中......");//1、使用DatagramSocket 指定端口创建接收端DatagramSocket server=new DatagramSocket(9999);//2、准备容器封装成DatagramPacket包裹byte[]container=new byte[1024*60];DatagramPacket packet=new DatagramPacket(container,0,container.length);//3、阻塞式接受包裹receive(DatagramPacket p)server.receive(packet);//4、分析数据//byte[]getData()//getLength()byte[]datas=packet.getData();int len=datas.length;System.out.println(new String(datas,0,len));//5、释放资源}}
UDP案例(一):文件发送
** IOUtils类* 文件(图片)的拷贝:* 文件——>程序——>字节数组——>文件*/public class IOUtils {/*** 1、图片到字节数组:* 1、图片到程序:FileInputStream* 2、程序到字节数组:ByteArrayOutputStream*/public static byte[] fileToByteArray(String path) {//创建源与目的地File src = new File(path);byte[] dest = null;//选择流InputStream is = null;ByteArrayOutputStream baos = null;try {is = new FileInputStream(src);baos = new ByteArrayOutputStream();//操作byte[] flush = new byte[1024 * 10];int len = -1;while ((len = is.read(flush)) != -1) {baos.write(flush, 0, len);}baos.flush();return baos.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}if(is!=null){try {is.close();} catch (IOException e) {e.printStackTrace();}}return null;}/*** 1、字节数组到文件* 1、字节数组到程序:ByteArrayInputStream* 2、程序到文件:FileOutputStream*/public static void byteArrayToFile(byte[]src,String filePath){File dest=new File(filePath);InputStream is=null;OutputStream os=null;is=new ByteArrayInputStream(src);try {os=new FileOutputStream(dest);int len=-1;byte[]flush=new byte[10];while ((len=is.read(flush))!=-1){os.write(flush,0,len);}os.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}if(os!=null){try {os.close();} catch (IOException e) {e.printStackTrace();}}}}
/***UdpFileClient类*文件上传:发送端* 发送图片文件* 图片文件大小不能太大*/public class UdpFileClient {public static void main(String[] args) throws Exception {System.out.println("发送方启动中......");//1、使用DatagramSocket对象 指定端口创建发送端DatagramSocket client=new DatagramSocket(8888);//准备数据转成字节数组byte []datas=IOUtils.fileToByteArray("lib/img/QQ.png");//3、封装成DatagramPacket包裹,需要指定目的地DatagramPacket packet=new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",9999));//4、发送包裹send(DatagramPacket p)client.send(packet);//5、释放资源client.close();}}
/*** UdpFileServer类* 接收端:文件存储* 接收保存图片*/public class UdpFileServer {public static void main(String[] args) throws Exception {System.out.println("接收方启动中......");//1、使用DatagramSocket 指定端口创建接收端DatagramSocket server=new DatagramSocket(9999);//2、准备容器封装成DatagramPacket包裹byte[]container=new byte[1024*60];DatagramPacket packet=new DatagramPacket(container,0,container.length);//3、阻塞式接受包裹receive(DatagramPacket p)server.receive(packet);//4、分析数据//byte[]getData()//getLength()byte[]datas=packet.getData();int len=datas.length;IOUtils.byteArrayToFile(datas,"lib/img/QQ_copy.png");//5、释放资源server.close();}}
UDP案例(二):聊天室
/*** UdpTalkClient类* 多次交流:发送端* 1、使用DatagramSocket 指定端口创建发送端* 2、准备数据转成字节数组* 3、封装成DatagramPacket包裹,需要指定目的地(IP+端口)* 4、发送包裹send(DatagramPacket p)* 5、释放资源*/public class UdpTalkClient {public static void main(String[] args) throws Exception {System.out.println("发送方启动中......");//1、使用DatagramSocket对象 指定端口创建发送端DatagramSocket client=new DatagramSocket(8888);//准备数据转成字节数组BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));while (true){String data=reader.readLine();byte[]datas=data.getBytes();//3、封装成DatagramPacket包裹,需要指定目的地DatagramPacket packet=new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",9999));//4、发送包裹send(DatagramPacket p)client.send(packet);if(data.equals("bye")){//System.out.println(Arrays.toString(data.getBytes()));break;}}//5、释放资源client.close();}}
/*** UdpTalkServer类* 多次交流:接收端* 1、使用DatagramSocket 指定端口创建接收端* 2、准备容器封装成DatagramPacket包裹* 3、阻塞式接受包裹receive(DatagramPacket p)* 4、分析数据* byte[]getData()* getLength()* 5、释放资源*/public class UdpTalkServer {public static void main(String[] args) throws Exception {System.out.println("接收方启动中......");//1、使用DatagramSocket 指定端口创建接收端DatagramSocket server=new DatagramSocket(9999);//2、准备容器封装成DatagramPacket包裹while (true){byte[]container=new byte[1024*60];DatagramPacket packet=new DatagramPacket(container,0,container.length);//3、阻塞式接受包裹receive(DatagramPacket p)server.receive(packet);//4、分析数据//byte[]getData()//getLength()byte[]datas=packet.getData();int len=datas.length;String data=new String(datas,0,len);System.out.println(data);//System.out.println(Arrays.toString(data.getBytes()));if(data.equals("bye")){break;}}//5、释放资源server.close();}}
UDP案例(三):多线程实现双向聊天
/*** 加入多线程实现双向交流,模拟在线咨询* 学生端*/public class TalkStudent {public static void main(String[] args) {new Thread(new UdpTalkSend(6666,"localhost",8888)).start();//发送new Thread(new UdpTalkReceive(9999,"马老师")).start(); //接收}}
/*** 加入多线程实现双向交流,模拟在线咨询* 教师端*/public class TalkTeacher {public static void main(String[] args) {new Thread(new UdpTalkReceive(8888,"扒鸡")).start(); //接收new Thread(new UdpTalkSend(5555,"localhost",9999)).start();//发送}}
/*** 面向对象+多线程实现Talk* 发送端*/public class UdpTalkSend implements Runnable{//1、使用DatagramSocket对象 指定端口创建发送端private DatagramSocket client;private BufferedReader reader;private String toIP;private int toPort;public UdpTalkSend(int port, String toIP, int toPort) {this.toIP=toIP;this.toPort=toPort;try {//1、使用DatagramSocket对象 指定端口创建发送端client=new DatagramSocket(port);//准备数据转成字节数组reader=new BufferedReader(new InputStreamReader(System.in));} catch (SocketException e) {e.printStackTrace();}}@Overridepublic void run() {while (true){String data= null;try {data = reader.readLine();byte[]datas=data.getBytes();//3、封装成DatagramPacket包裹,需要指定目的地DatagramPacket packet=new DatagramPacket(datas,0,datas.length,new InetSocketAddress(this.toIP,this.toPort));//4、发送包裹send(DatagramPacket p)client.send(packet);if(data.equals("bye")){//System.out.println(Arrays.toString(data.getBytes()));break;}} catch (IOException e) {e.printStackTrace();}}//5、释放资源client.close();}}
/*** 接收端*/public class UdpTalkReceive implements Runnable{private DatagramSocket server;private String from;public UdpTalkReceive(int port,String from) {this.from=from;try {//1、使用DatagramSocket 指定端口创建接收端server = new DatagramSocket(port);} catch (SocketException e) {e.printStackTrace();}}@Overridepublic void run() {//2、准备容器封装成DatagramPacket包裹while (true){byte[]container=new byte[1024*60];DatagramPacket packet=new DatagramPacket(container,0,container.length);// 3、阻塞式接受包裹receive(DatagramPacket p)try {server.receive(packet);//4、分析数据byte[]datas=packet.getData();int len=packet.getLength();String data=new String(datas,0,len);System.out.println(from+": "+data);//System.out.println(Arrays.toString(data.getBytes()));if (data.equals("bye")){break;}} catch (IOException e) {e.printStackTrace();}}//5、释放资源server.close();}}
TCP编程
概述
通信双方需要建立连接请求—响应模式
连接建立时双方存在主次之分(第一次主动发起通讯的为客户端)
114查号台
利用IO流实现数据传输
Request和Response
编写流程:Client端
1、Socket对象(创建对象即建立连接:使用Socket创建客户端+服务的Ip地址和端口)
2、IO输入输出流(发送\接收消息)
3、关闭资源
代码片段
/*** 创建客户端* 1、建立连接:使用Socket创建客户端+服务的Ip地址和端口* 2、操作:输入输出流操作* 3、释放资源*/public class Client {public static void main(String[] args) throws IOException {System.out.println("------Client------");//1、建立TCP连接:使用Socket创建客户端+服务的Ip地址和端口(发起与服务端的TCP连接,用于3次握手)Socket client=new Socket("localhost",8888);//2、操作:输入输出流操作DataOutputStream dos=new DataOutputStream(client.getOutputStream());String msg="山东扒鸡,买一送一!";dos.writeUTF(msg);dos.flush();//3、释放资源dos.close();client.close();}}
编写流程:Server端
1、设置服务器监听对象(ServerSocket)
2、建立连接:Socket对象,接收一个客户端
3、阻塞式等待客户端连接:accept()方法
4、IO输入输出流,接收\返回消息
常用方法
**1、getInputStream():返回此套接字的输入流。
2、getOutputStream():返回此套接字的输出流。**
代码片段
/*** 创建服务器* 1、指定端口 使用ServerSocket创建服务器* 2、阻塞式等待连接accept* 3、操作:输入输出流操作* 4、释放资源*/public class Server {public static void main(String[] args) throws IOException {System.out.println("------Server------");//1、指定端口 使用ServerSocket创建服务器(建立了TCP套接字,用于和客户端Client的3次握手,也叫欢迎套接字)ServerSocket serverSocket=new ServerSocket(8888);//2、阻塞式等待连接accept,返回Socket对象,建立了一个新连接(新套接字:连接套接字)。Socket client=serverSocket.accept();System.out.println("一个客户端建立了连接。。。");//3、操作:输入输出流操作DataInputStream dis=new DataInputStream(client.getInputStream());String msg=dis.readUTF();System.out.println(msg);//4、释放资源dis.close();client.close();}}
TCP文件上传案例
/*** 客户端* 上传文件* 1、建立连接:使用Socket创建客户端+服务的Ip地址和端口* 2、操作:输入输出流操作* 3、释放资源*/public class FileClient {public static void main(String[] args) throws IOException {System.out.println("------Client------");//1、建立连接:使用Socket创建客户端+服务的Ip地址和端口Socket client=new Socket("localhost",8888);//2、操作:输入输出流操作(文件拷贝上传)InputStream is=new BufferedInputStream(new FileInputStream("lib/img/Socket.png"));OutputStream os=new BufferedOutputStream(client.getOutputStream());byte[]flush=new byte[1024];int len=-1;while ((len=is.read(flush))!=-1){os.write(flush,0,len);}os.flush();//3、释放资源os.close();is.close();client.close();}}
/*** 服务端* 存储文件* 创建服务器* 1、指定端口 使用ServerSocket创建服务器* 2、阻塞式等待连接accept* 3、操作:输入输出流操作* 4、释放资源*/public class FileServer {public static void main(String[] args) throws IOException {System.out.println("------Server------");//1、指定端口 使用ServerSocket创建服务器ServerSocket serverSocket=new ServerSocket(8888);//2、阻塞式等待连接accept,返回Socket对象,建立了连接。Socket client=serverSocket.accept();System.out.println("一个客户端建立了连接。。。");//3、操作:输入输出流操作 (文件拷贝存储)InputStream is=new BufferedInputStream(client.getInputStream());OutputStream os=new BufferedOutputStream(new FileOutputStream("lib/img/Socket_copy.png"));byte[]flush=new byte[1024];int len=-1;while ((len=is.read(flush))!=-1){os.write(flush,0,len);}os.flush();//4、释放资源os.close();is.close();client.close();}}
模拟登录案例
/*** 模拟登录(双向)* 创建客户端* 1、建立连接:使用Socket创建客户端+服务的Ip地址和端口* 2、操作:输入输出流操作* 3、释放资源*/public class LoginClient {public static void main(String[] args) throws IOException {//1、建立连接:使用Socket创建客户端+服务的Ip地址和端口Socket client=new Socket("Localhost",5555);BufferedReader console=new BufferedReader(new InputStreamReader(System.in));System.out.println("请输入用户名:");String uname=console.readLine();System.out.println("请输入密码:");String upwd=console.readLine();//2、操作:输入输出流操作DataOutputStream dos=new DataOutputStream(client.getOutputStream());dos.writeUTF("uname="+uname+"&"+"upwd="+upwd);dos.flush();DataInputStream dis=new DataInputStream(client.getInputStream());String result=dis.readUTF();System.out.println(result);//3、释放资源dos.close();dis.close();client.close();}}
/*** 模拟登录(双向)* 创建服务器* 1、指定端口 使用ServerSocket创建服务器* 2、阻塞式等待连接accept* 3、操作:输入输出流操作* 4、释放资源*/public class LoginServer {public static void main(String[] args) throws IOException {//1、指定端口 使用ServerSocket创建服务器ServerSocket server=new ServerSocket(5555);//2、阻塞式等待连接acceptSocket client=server.accept();System.out.println("一个客户建立了连接");//3、操作:输入输出流操作DataInputStream dis=new DataInputStream(client.getInputStream());String datas=dis.readUTF();String uname="";String upwd="";//4、分析数据String[]dataArray=datas.split("&");for(String info:dataArray){String[]userInfo=info.split("=");if(userInfo[0].equals("uname")){uname=userInfo[1];System.out.println("你的用户名是:"+userInfo[1]);}else if(userInfo[0].equals("upwd")){upwd=userInfo[1];System.out.println("你的密码是:"+userInfo[1]);}}DataOutputStream dos=new DataOutputStream(client.getOutputStream());if(uname.equals("扒鸡")&&upwd.equals("123")){ //验证成功dos.writeUTF("登录成功,欢迎回来!");}else{ //验证失败dos.writeUTF("密码或者用户名错误!");}dos.flush();//5、释放资源dis.close();client.close();}}
模拟登录(双向)+多个客户端请求
/*** 模拟登录(双向)+多个客户端请求* 创建客户端* 1、建立连接:使用Socket创建客户端+服务的Ip地址和端口* 2、操作:输入输出流操作* 3、释放资源*/public class LoginMultiClient {public static void main(String[] args) throws IOException {//1、建立连接:使用Socket创建客户端+服务的Ip地址和端口System.out.println("------Client------");Socket client=new Socket("Localhost",5555);new Send(client).send();new Receive(client).receive();client.close();}//发送数据static class Send{private Socket client;private DataOutputStream dos;private BufferedReader console;private String msg;public Send(Socket client) {console=new BufferedReader(new InputStreamReader(System.in));this.msg=init();this.client = client;try {dos=new DataOutputStream(client.getOutputStream());} catch (IOException e) {e.printStackTrace();}}//初始化private String init(){try {System.out.println("请输入用户名:");String uname=console.readLine();System.out.println("请输入密码:");String upwd=console.readLine();return "uname="+uname+"&"+"upwd="+upwd;} catch (IOException e) {e.printStackTrace();}return null;}private void send(){try {dos.writeUTF(msg);dos.flush();} catch (IOException e) {e.printStackTrace();}}}//接收数据static class Receive{private Socket client;private DataInputStream dis;public Receive(Socket client) {this.client = client;try {dis=new DataInputStream(client.getInputStream());} catch (IOException e) {e.printStackTrace();}}public void receive(){String result= null;try {result = dis.readUTF();} catch (IOException e) {e.printStackTrace();}System.out.println(result);}}}
/*** 模拟登录(双向)+多个客户端请求* 创建服务器* 1、指定端口 使用ServerSocket创建服务器* 2、阻塞式等待连接accept* 3、操作:输入输出流操作* 4、释放资源*/public class LoginMultiServer {public static void main(String[] args) throws IOException {System.out.println("------Server------");//创建服务器,监听端口ServerSocket server=new ServerSocket(5555);Boolean isRunning=true;while (isRunning){Socket client=server.accept();System.out.println("一个客户建立了连接");new Thread(new Channel(client)).start();}server.close();}//一个Channel就代表了一个客户端static class Channel implements Runnable{private Socket client;//输入流private DataInputStream dis;//输出流private DataOutputStream dos;//构造器public Channel(Socket client) {this.client=client;try{//输入dis=new DataInputStream(client.getInputStream());//输出dos=new DataOutputStream(client.getOutputStream());}catch (IOException e) {e.printStackTrace();release();}}//接收数据private String Receive(){String datas = null;try {datas = dis.readUTF();} catch (IOException e) {e.printStackTrace();}return datas;}//发送数据private void send(String msg){try {dos.writeUTF(msg);dos.flush();} catch (IOException e) {e.printStackTrace();}}//释放资源private void release(){try {if(null!=dis){dis.close();}} catch (IOException e) {e.printStackTrace();}try {if(null!=dos){dos.close();}} catch (IOException e) {e.printStackTrace();}try {if(null!=client){client.close();}} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {String uname="";String upwd="";//4、分析数据String[]dataArray=Receive().split("&");for(String info:dataArray){String[]userInfo=info.split("=");if(userInfo[0].equals("uname")){uname=userInfo[1];System.out.println("你的用户名是:"+userInfo[1]);}else if(userInfo[0].equals("upwd")){upwd=userInfo[1];System.out.println("你的密码是:"+userInfo[1]);}}if(uname.equals("扒鸡")&&upwd.equals("123")){ //验证成功send("登录成功,欢迎回来!");}else{ //验证失败send("密码或者用户名错误!");}//释放资源release();}}}
