【网络编程】
主要内容
- 网络通信三要素
- TCP通信UDP通信
- Socket套接字
- ServerSocket
第一章 网络通信三要素
IP地址
InetAddress:多个静态方法
- 1.getLocalHost:本机
- 2.getByName:根据域名DNS | IP地址 —> IP
两个成员方法:
- 1、getHostAddress:返回地址
- 2、getHostName:返回计算机名
端口号
- 公认端口 0-1023 比如80端口分配www,21端口分配geiFTP
- 注册端口 1024-49151 分配给用户进程或应用程序
- 动态/私有端口 49152—65535
URL
主要四部分组成:
协议 存放资源的主机域名 端口号 资源文件
传输协议
常见的两种协议有:TCP和UDP
TCP:一种面向连接(连接导向)的、可靠的、基于字节流的运输层通信协议。
特点:面向连接、点到点的通信、可靠性高、占用系统资源多、效率低
UDP:一种无连接的传输层协议,提供面向事务的简单不可靠信息传输服务
特点:
- 非面向连接,传输不可靠,可能丢失
- 发送不管对方是否准备好,接收方收到也不确认
- 可以广播发送,例如直播
- 非常简单的协议,开销小
第二章 UDP编程(实现案例网上在线咨询)
案例一:简单的文字UDP传输
单次发送,单次接收
客户端代码:
package com.sxt.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/**
* 基本流程: 发送端
* 1、使用DatagramSocket 指定端口 创建发送端
* 2、准备数据 一定转成字节数组
* 3、 封装成DatagramPacket 包裹,需要指定目的地
* 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);
//2、准备数据 一定转成字节数组
String data = "我爱java";
byte[] datas = data.getBytes();
//3、 封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
new InetSocketAddress("localhost",6666));
//4、发送包裹send(DatagramPacket p) *
client.send(packet);
// 5、释放资源
client.close();
}
}
服务端代码:
package com.sxt.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 基本流程: 接收端
* Address already in use: Cannot bind 同一个协议下端口不允许冲突
* 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(6666);
// 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 = packet.getLength();
System.out.println(new String(datas,0,len));
// 5、释放资源
server.close();
}
}
输出结果:
接收方启动中....
我爱java
DatagramSocket类 用于表示发送的和接收数据包的套接字。
DatagramPacket类 数据包
案例二:网上在线咨询UDP
多次发送,多次接收
使用面向对象封装发送端
package com.sxt.udp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/**
* 发送端: 使用面向对象封装
*
*
*/
public class TalkSend implements Runnable {
private DatagramSocket client ;
private BufferedReader reader;
private String toIP ;
private int toPort ;
//指定本机发送端口,接收方ip,和接收方端口
public TalkSend(int port,String toIP,int toPort) {
this.toIP = toIP;
this.toPort=toPort;
try {
//指定本机发送端的端口
client=new DatagramSocket(port);
//读取控制台输入数据
reader =new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
String data;
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")) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 5、释放资源
client.close();
}
}
使用面向对象封装接收端
package com.sxt.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* 接收端: 使用面向对象封装
*
*
*/
public class TalkReceive implements Runnable {
private DatagramSocket server ;
private String from ;
//本地的接收端端口,和发送人名
public TalkReceive(int port,String from) {
this.from = from ;
try {
//创建一个本地的接收端端口
server=new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
// 2、准备容器 封装成DatagramPacket 包裹
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);
if(data.equals("bye")) {
break;
}
} catch (IOException e) {
e.printStackTrace();
} `在这里插入代码片`
}
// 5、释放资源
server.close();
}
}
然后创建学生端
package com.sxt.udp;
/**
* 加入多线程,实现双向交流 模拟在线咨询
*
*
*/
public class TalkStudent {
public static void main(String[] args) {
new Thread(new TalkSend(7777,"localhost",9999)).start(); //发送
new Thread(new TalkReceive(8888,"老师")).start(); //接收
}
}
创建老师端
package com.sxt.udp;
/**
* 加入多线程,实现双向交流 模拟在线咨询
*
*
*/
public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkReceive(9999,"学生")).start(); //接收
new Thread(new TalkSend(5555,"localhost",8888)).start(); //发送
}
}
好了 上述四个类就可实现双向通信了。
UDP是只管发送不管你接没接收到。当只启动发送端时,数据丢失,但是不会错。
第三章 TCP编程(实现多用户登录功能)
TCP编程详细步骤(通信原理)
- 服务器创建ServerSocket,在指定端口监听并处理请求
- 客户端创建Socket,向服务器发送请求
案例一:TCP文字发送
单方发送,一次接收。
客户端
package com.sxt.tcp;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 熟悉流程
* 创建客户端
* 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
* 2、操作: 输入输出流操作
* 3、释放资源
*
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("-----Client-----");
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client =new Socket("localhost",8888);
//2、操作: 输入输出流操作
DataOutputStream dos =new DataOutputStream(client.getOutputStream());
String data ="hello";
dos.writeUTF(data);
dos.flush();
//3、释放资源
dos.close();
client.close();
}
}
服务端
package com.sxt.tcp;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 熟悉流程
* 创建服务器
* 1、指定端口 使用ServerSocket创建服务器
* 2、阻塞式等待连接 accept
* 3、操作: 输入输出流操作
* 4、释放资源
*
*/
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server =new ServerSocket(8888);
// 2、阻塞式等待连接 accept
Socket client =server.accept();
System.out.println("一个客户端建立了连接");
// 3、操作: 输入输出流操作
DataInputStream dis =new DataInputStream(client.getInputStream());
String data =dis.readUTF();
System.out.println(data);
// 4、释放资源
dis.close();
client.close();
server.close();
}
}
案例二:使用TCP实现文件上传
文件服务器
package com.sxt.tcp;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 存储文件
* 创建服务器
* 1、指定端口 使用ServerSocket创建服务器
* 2、阻塞式等待连接 accept
* 3、操作: 输入输出流操作
* 4、释放资源
*
*/
public class FileServer {
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server =new ServerSocket(8888);
// 2、阻塞式等待连接 accept
Socket client =server.accept();
System.out.println("一个客户端建立了连接");
// 3、操作: 文件拷贝 存储
InputStream is =new BufferedInputStream(client.getInputStream());
OutputStream os =new BufferedOutputStream(new FileOutputStream("src/tcp.png"));
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();
// 4、释放资源
client.close();
server.close();
}
}
客户端
package com.sxt.tcp;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 上传文件
* 创建客户端
* 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
* 2、操作: 输入输出流操作
* 3、释放资源
*
*/
public class FileClient {
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("-----Client-----");
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client =new Socket("localhost",8888);
//2、操作: 拷贝 上传
InputStream is =new BufferedInputStream(new FileInputStream("src/ndl.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();
}
}
案例三:TCP多用户登录
客户端
package com.sxt.tcp;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 模拟登录 多个客户端请求
* 创建客户端
* 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
* 2、操作: 输入输出流操作
* 3、释放资源
*/
public class LoginMultiClient {
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("-----Client-----");
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client =new Socket("localhost",8888);
//2、操作: 输入输出流操作 先请求后响应
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.print("请输入用户名:");
String uname =console.readLine();
System.out.print("请输入密码:");
String upwd =console.readLine();
return "uname="+uname+"&"+"upwd="+upwd;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
public 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;
try {
result = dis.readUTF();
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务端
package com.sxt.tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 模拟登录 多个客户端请求
* 创建服务器
* 1、指定端口 使用ServerSocket创建服务器
* 2、阻塞式等待连接 accept
* 3、操作: 输入输出流操作
* 4、释放资源
*
*/
public class LoginMultiServer {
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server =new ServerSocket(8888);
boolean isRunning =true;;
// 2、阻塞式等待连接 accept
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 ="";
try {
datas = dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
}
return datas;
}
//释放资源
private void release() {
// 4、释放资源
try {
if(null != dos) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != dis) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != client) {
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//发送数据
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// 3、操作: 输入输出流操作
String uname ="";
String upwd ="";
//分析
String[] dataArray = receive().split("&");
for(String info:dataArray) {
String[] userInfo =info.split("=");
if(userInfo[0].equals("uname")) {
System.out.println("你的用户名为:"+userInfo[1]);
uname = userInfo[1];
}else if(userInfo[0].equals("upwd")) {
System.out.println("你的密码为:"+userInfo[1]);
upwd = userInfo[1];
}
}
if(uname.equals("shsxt") && upwd.equals("laopei")) { //成功
send("登录成功,欢迎回来");
}else { //失败
send("用户名或密码错误");
}
release();
}
}
}
案例四:TCP手写聊天室
代码过长点击下面的链接进入查看源码