网络编程: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();
}
}
@Override
public 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();
}
}
@Override
public 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、阻塞式等待连接accept
Socket 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();
}
}
@Override
public 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();
}
}
}