网络编程
网络编程主要用于解决计算机与计算机(手机、平板…)之间的数据传输问题。
1.InetAddress(IP类)
方法:
方法 | 描述 |
---|---|
getLocalHost() | 获取本机的IP地址对象 |
getByName(“IP或者主机名”) | 根据一个IP地址的字符串形式或者是一个主机名生成一个IP地址对象。 (用于获取别人的IP地址对象) |
getHostAddress() | 返回一个IP地址的字符串表示形式。 |
getHostName() | 返回计算机的主机名。 |
getAddress() | 返回此InetAddress对象的原始 IP 地址 |
2.端口号
端口号是没有类描述的。
端口号的范围: 0~65535
从0到1023,系统紧密绑定于一些服务。
1024~65535 我们可以使用
端口常见报错 java.net.BindException: 端口被占用
在cmd中查看端口号占用
命令 | 描述 |
---|---|
netstat -ano | 查看所有的端口 |
netstat -ano |findstr “8080” | 查看指定的端口 |
tasklist|findstr “PID” | 查看占用端口号的进程 |
3.网络通讯协议
每个网络程序都有自己所处理的特定格式数据,如果接收到的数据不符合指定的格式,那么就会被当成垃圾数据丢弃。(加密)
在java网络通讯是通过Socket(插座)通讯技术实现的,要求通讯的两台器都必须要安装Socket。不同的协议就有不同的插座(Socket)
1. 服务端开启并监听一个端口,时刻等待着客户端的连接请求
2. 客户端知道服务端的ip地址和监听端口号,发出请求到服务端
Socket类方法:
构造方法 | 描述 |
---|---|
Socket(InetAddress address, int port) | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号 |
主要方法 | 描述 |
---|---|
getInetAddress() | 返回套接字连接的地址 |
public InputStream getInputStream() | 返回此套接字的输入流 |
public OutputStream getOutputStream() | 返回此套接字的输出流 |
方法 | 描述 |
---|---|
void bind(SocketAddress bindpoint) | 将套接字绑定到本地地址。 |
void close() | 关闭此套接字。 |
void connect(SocketAddress endpoint) | 将此套接字连接到服务器。 |
void connect(SocketAddress endpoint, int timeout) | 将此套接字连接到具有指定超时值的服务器。 |
SocketChannel getChannel() | 返回与此套接字相关联的唯一的SocketChannel对象(如果有)。 |
InetAddress getInetAddress() | 返回套接字所连接的地址。 |
InputStream getInputStream() | 返回此套接字的输入流。 |
boolean getKeepAlive() | 测试是否启用了 SO_KEEPALIVE 。 |
InetAddress getLocalAddress() | 获取套接字所绑定的本地地址。 |
int getLocalPort() | 返回此套接字绑定到的本地端口号。 |
SocketAddress getLocalSocketAddress() | 返回此套接字绑定到的端点的地址。 |
boolean getOOBInline() | 测试是否启用了 SO_OOBINLINE 。 |
T getOption(SocketOption name) | 返回套接字选项的值。 |
OutputStream getOutputStream() | 返回此套接字的输出流。 |
int getPort() | 返回此套接字连接到的远程端口号。 |
int getReceiveBufferSize() | 获取此 Socket的 SO_RCVBUF选项的值,即该平台在此 Socket上输入的缓冲区大小。 |
SocketAddress getRemoteSocketAddress() | 返回此套接字连接到的端点的地址,如果未连接,则 null 。 |
boolean getReuseAddress() | 测试是否启用了 SO_REUSEADDR 。 |
int getSendBufferSize() | 获取此 Socket的 SO_SNDBUF选项的值,即平台在此 Socket上输出使用的缓冲区大小。 |
int getSoLinger() | 退货设置为 SO_LINGER 。 |
int getSoTimeout() | 退货设置为SO_TIMEOUT 。 0返回意味着该选项被禁用(即无限超时)。 |
boolean getTcpNoDelay() | 测试是否启用了 TCP_NODELAY 。 |
int getTrafficClass() | 在从此Socket发送的数据包的IP头中获取流量类或服务类型 |
boolean isBound() | 返回套接字的绑定状态。 |
boolean isClosed() | 返回套接字的关闭状态。 |
boolean isConnected() | 返回套接字的连接状态。 |
boolean isInputShutdown() | 返回套接字连接的一半是否关闭。 |
boolean isOutputShutdown() | 返回套接字连接的写半是否关闭。 |
void sendUrgentData(int data) | 在套接字上发送一个字节的紧急数据。 |
void setKeepAlive(boolean on) | 启用/禁用 SO_KEEPALIVE 。 |
void setOOBInline(boolean on) | 启用/禁用 SO_OOBINLINE (接收TCP紧急数据)默认情况下,此选项被禁用,并且在套接字上接收的TCP紧急数据被静默地丢弃。 |
Socket setOption(SocketOption name, T value) | 设置套接字选项的值。 |
void setPerformancePreferences(int connectionTime, int latency, int bandwidth) | 设置此套接字的性能首选项。 |
void setReceiveBufferSize(int size) | 设置 SO_RCVBUF选项,此规定值 Socket 。 |
void setReuseAddress(boolean on) | 启用/禁用 SO_REUSEADDR套接字选项。 |
void setSendBufferSize(int size) | 设置 SO_SNDBUF选项,此规定值 Socket 。 |
static void setSocketImplFactory(SocketImplFactory fac) | 设置应用程序的客户端套接字实现工厂。 |
void setSoLinger(boolean on, int linger) | 启用/禁用 SO_LINGER具有指定的逗留时间(以秒为单位)。 |
void setSoTimeout(int timeout) | 启用/禁用 SO_TIMEOUT与指定的超时,以毫秒为单位。 |
void setTcpNoDelay(boolean on) | 启用/禁用 TCP_NODELAY (禁用/启用Nagle的算法)。 |
void setTrafficClass(int tc) | 在从此Socket发送的数据包的IP头中设置流量类或服务类型字节。 |
void shutdownInput() | 将此套接字的输入流放置在“流的末尾”。 |
void shutdownOutput() | 禁用此套接字的输出流。 |
Set<SocketOption<?>> supportedOptions() | 返回此套接字支持的一组套接字选项。 |
String toString() | 将此套接字转换为 String 。 |
UPD通讯协议
UDP通讯协议的特点:
1.将数据极封装为数据包,面向无连接
2.每个数据包大小限制在64K中,有大小限制
3.因为无连接,所以不可靠
4.因为不需要建立连接,所以速度快
5.udp 通讯是不分服务端与客户端的,只分发送端与接收端。
在udp协议中,有一个IP地址称作为广播地址,广播地址就是主机号为255地址。 给广播 IP地址发送消息的时候,在同一个网络段的机器都可以接收到信息。
UDP协议下的Socket:
方法 | 描述 |
---|---|
DatagramSocket(udp插座服务) | 用来发送和接受数据包 |
DatagramPacket(数据包类) | 表示数据包 |
DatagramPacket(buf, length, address, port)
buf: 发送的数据内容
length : 发送数据内容的大小
address : 发送的目的IP地址对象
port : 端口号
发送端的使用步骤:client
- 1.建立UDP的服务
DatagramSocket client= new DatagramSocket();
- 2.准备数据,把数据封装到数据包中发送
String data = "这个是要发送的数据";
//创建了一个数据包, 发送端的数据包要带上ip地址与端口号
//注意:传输的都是字节流,转换为字节数组
DatagramPacket packet = new DatagramPacket(data.getBytes(),
data.getBytes().length,InetAddress.getByName("IP或者主机名") , 端口号);
- 3.发送数据包
client.send(packet); //使用send发送数据包
- 4.关闭资源 —实际上就是释放占用的端口号
client.close();
接收端的使用步骤:server
- 1.建立UDP的服务 ,并且要监听一个端口。这个端口号需要与client中相同才能建立连接
DatagramSocket server= new DatagramSocket(要监听的端口号);
- 2.准备空的数据包用于存放数据。
byte[] buf = new byte[1024]; //接收的容器 DatagramPacket packet= new DatagramPacket(buf,0, buf.length); // 1024
- 3.接收数据包
//receive是一个阻塞型的方法,没有接收到数据包之前会一直等待。数据是存储到了byte的字节数组中。 server.receive(packet); System.out.println("接收端接收到的数据:"+ new String(buf,0,packet.getLength())); // getLength() 获取数据包存储了几个字节。
- 4.关闭资源
socket.close();
package com.gqz.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/**
* 基本流程:发送端
* 1、使用DatagramSocket 指定端口 创建接受端
* 2、准备数据,一定要转成字节数组
* 3、封装成DatagramPacket包裹,需要指定目的地
* 4、发送包裹send (DatagramPacket p)
* byte[] getData()
* getLength()
* 5、释放资源
* @ClassName: UdpClient
* @Description: TODO(这里用一句话描述这个类的作用)
* @author ganquanzhong
* @date 2019年7月12日 下午5:29:02
*/
public class UdpClient {
public static void main(String[] args) throws Exception {
System.out.println("client 发送方启动中......");
// 1、使用DatagramSocket 指定端口 创建接受端
DatagramSocket client = new DatagramSocket(8888);
// 2、准备数据,一定要转成字节数组
String data = "模拟UPD发送数据,请求登录(username ,password)";
byte[] datas = data.getBytes(); //字符串转成字节数组
// 3、封装成DatagramPacket包裹,需要指定目的地本机
DatagramPacket packet = new DatagramPacket(datas, datas.length, new InetSocketAddress("localhost", 9999));
// 4、发送包裹send (DatagramPacket p)
client.send(packet);
// byte[] getData()
// getLength()
// 5、释放资源
client.close();
}
}
UpdServer.class
package com.gqz.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 基本流程:接收端
* 1、使用DatagramSocket 指定端口 创建接受端
* 2、准备容器 封装成DatagramPacket 包裹
* 3、阻塞式接收包裹 recevice (DatagramPacket p)
* 4、分析数据
* byte[] getData()
* getLength()
* 5、释放资源
*
* @ClassName: UdpServer
* @Description: TODO(这里用一句话描述这个类的作用)
* @author ganquanzhong
* @date 2019年7月12日 下午5:29:02
*/
public class UdpServer {
public static void main(String[] args) throws Exception {
System.out.println("server接收 这里是服务端,接收数据启动中..........");
// 1、使用DatagramSocket 指定端口 创建接受端
DatagramSocket server = new DatagramSocket(9999);
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container =new byte[1024*60];//最大60K
DatagramPacket packet = new DatagramPacket(container, 0,container.length);
// 3、阻塞式接收包裹 recevice (DatagramPacket p)
server.receive(packet); //阻塞式
// 4、分析数据
// byte[] getData()
// getLength()
byte[] datas = packet.getData();
System.out.println(packet.getLength());
System.out.println(packet.getOffset());
System.out.println(packet.getPort());
System.out.println(packet.getAddress());
System.out.println(packet.getSocketAddress());
System.out.println(new String(datas));
// 5、释放资源
server.close();
}
}
TCP通讯协议
TCP通讯协议特点:
tcp是基于IO流进行数据的传输的,面向连接。(需要转换成字节数组)
tcp进行数据传输的时候是没有大小限制的。
tcp是面向连接,通过三次握手的机制保证数据的完整性。可靠协议。
tcp是面向连接的,所以速度慢。
tcp是区分客户端与服务端的。
tcp协议下的Socket:
- Socket(客户端类) tcp的客户端一旦启动马上要与服务端进行连接。
- ServerSocket(服务端类) 使用在服务器端
public class ServerSocket
extends Object
implements Closeable
这个类实现了服务器套接字。 服务器套接字等待通过网络进入的请求。 它根据该请求执行一些操作,然后可能将结果返回给请求者。
服务器套接字的实际工作由SocketImpl类的实例执行。 应用程序可以更改创建套接字实现的套接字工厂,以配置自己创建适合本地防火墙的套接字。
ServerSocket服务端使用步骤:
try{
//1.建立Tcp的服务端,打开并且监听一个端口。
ServerSocket server= new ServerSocket(监听的端口号); //ServerSocket服务端套接字
//2. 等待接受客户端的连接产生一个Socket
//accept()接受客户端的连接该方法是一个阻塞型的方法,没有客户端与其连接时会一直等待下去。
Socket client = server.accept();
//获取输入输出流对象
InputStream is = client.getInputStream();
OutputStream os = client.getOutputStream();
//3.1 获取输入流对象,读取客户端发送的内容。
byte[] buf = new byte[1024];
int length = 0;
length = is.read(buf);
System.out.println("服务端接收:"+ new String(buf,0,length));
//3.2 获取socket输出流对象,向客户端发送数据
os.write("客户端你好!".getBytes());
//4.关闭资源
is.close():
os.close();
client.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
tcp的客户端使用步骤:
try{
//1.通过IP,端口 建立服务端连接
Socket client = new Socket(InetAddress.getByName("IP或者主机名"),端口号);
//2. 获取到Socket的输入、输出流对象
InputStream is = client.getInputStream();
OutputStream os = client.getOutputStream();
//3.1 利用输出流对象,向服务器写出数据。
os.write("服务端你好!".getBytes());
//3.2 利用输入流对象,读取服务端回送的数据。
byte[] buf = new byte[1024];
int length = is.read(buf);
System.out.println("客户端接收到的数据:"+ new String(buf,0,length));
//4.关闭资源
is.close();
os.close();
client.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
tcp字符流注意细节:
如果使用BuffrerdReader的readline方法一定要加上 才能把数据写出。
使用字符流一定要调用flush方法数据才会写出。
例:tcp字符流通信
客户端
//获取socket的输出流对象。
OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream());
//获取socket的输入流对象
BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//把数据写出给服务器
socketOut.write(发送给服务器的数据+"
"); //注意加上
socketOut.flush();
//读取服务端回送的数据
socketReader.readLine();
服务端
//获取到Socket的输入流对象
BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取到Socket输出流对象
OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream());
//读取客户端的数据
socketReader.readLine()
//回送给客户端数据
socketOut.write(发给客户端的数据+"
"); //注意加上
socketOut.flush();
package com.gqz.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创建客户端 (服务器地址和端口)
* 3、操作:输入输出流操作
* 4、释放资源
*/
public class LoginMultiClient {
public static void main(String[] args) throws UnknownHostException, IOException{
System.out.println("=====client=====");
//使用Socket创建客户端 (服务器地址和端口)
Socket client = new Socket("localhost",8888);
//请求服务器 request
new Request(client).send();
//服务器响应
new Response(client).recevie();
//3、释放资源
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @ClassName: Request
* @Description: 请求
* @author ganquanzhong
* @date 2019年7月16日 上午10:56:12
*/
static class Request{
private Socket client;
private BufferedReader console;
private DataOutputStream dos;
private String msg;
public Request(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 username =console.readLine();
System.out.print("密码:");
String password = console.readLine();
return "username="+username+"&"+"password="+password;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private void send() {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
*
* @ClassName: Response
* @Description: 响应
* @author ganquanzhong
* @date 2019年7月16日 上午10:56:27
*/
static class Response{
private Socket client;
private DataInputStream dis;
public Response(Socket client) {
this.client=client;
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void recevie() {
String result;
try {
result = dis.readUTF();
System.out.println(result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
LoginMultiServer.class
package com.gqz.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 {
boolean isRunning = true;
System.out.println("=====server=====");
//1、指定端口号 使用ServerSocket创建服务器
ServerSocket server = new ServerSocket(8888);
//2、阻塞式等待连接accept
while(isRunning) {
Socket client = server.accept();//建立连接返回一个socket
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 recevie() {
String datas = "";
try {
datas = dis.readUTF();
} catch (IOException e) {
// TODO Auto-generated catch block
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 != 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();
}
}
@Override
public void run() {
//3、操作 :输入输出流操作
//分析数据
String username ="";
String password ="";
String[] dataArray = recevie().split("&");
for(String info:dataArray) {
String[] userInfo = info.split("=");
if (userInfo[0].equals("username")) {
username=userInfo[1];
}else {
password=userInfo[1];
}
}
//判断
if (username.equals("gqz") && password.equals("admin")) {
send("登录成功,欢迎进入ForFuture系统!!");
}else {
send("用户名或密码错误!!");
}
//4、释放资源
release();
}
}
}
服务器端Chat.class
package com.gqz.chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 服务端,消息转发
* 多个客户可以正常收发消息
* @ClassName: Chat
* @Description: TODO(这里用一句话描述这个类的作用)
* @author ganquanzhong
* @date 2019年7月16日 下午3:01:34
*/
public class Chat {
static private CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<Chat.Channel>();
static int i=0;
public static void main(String[] args) throws IOException {
System.out.println("=====Server=====");
//1、指定端口号 使用ServerSocket创建服务器 监听端口9999
ServerSocket server = new ServerSocket(9999);
//2、阻塞式等待连接accept
while (true) {
Socket client = server.accept();
System.out.println("建立"+(++i)+"连接");
Channel c = new Channel(client);
all.add(c);//管理所有的成员
new Thread(c).start();
}
}
/**
*
* @ClassName: Channel
* @Description: 一个client代表一个channel
* @author ganquanzhong
* @date 2019年7月16日 下午5:49:02
*/
static class Channel implements Runnable{
private DataInputStream dis ;
private DataOutputStream dos ;
private Socket client;
private boolean isRunning;
private String name;
public Channel(Socket client) {
this.client=client;
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
isRunning=true;
//获取名称
this.name=receive();
this.send("欢迎来到ForFuture群聊系统!");
sendOthers(this.name+"上线了", true);
} catch (IOException e) {
System.out.println("---初始化错误----");
release();
e.printStackTrace();
}
}
//接收消息
private String receive() {
String msg="";
try {
msg= dis.readUTF();
} catch (IOException e) {
System.out.println("---接收消息错误----");
release();
e.printStackTrace();
}
return msg;
}
//发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println("---发送消息错误----");
release();
e.printStackTrace();
}
}
/**
* 群聊:获取自己的消息,发送给别人
* 私聊:约定数据格式 @:名称:msg
*
* @Title: sendOthers
* @Description: TODO(这里用一句话描述这个方法的作用)
* @author ganquanzhong
* @date 2019年7月17日 上午9:16:44
* @param msg
* @param isSys
*/
private void sendOthers(String msg,boolean isSys) {
boolean isPrivate = msg.startsWith("@");
if (isPrivate) {
//私聊
int idx1 = msg.indexOf(":");
int idx2 = msg.indexOf(":");
int idx = idx1 != -1?idx1:idx2;
String targetName = msg.substring(1, idx);
msg = msg.substring(idx+1);
for(Channel other :all) {
if (other.name.equals(targetName)) {
other.send("
"+new SimpleDateFormat("YYYY-MM-dd HH:MM:ss SSS").format(new Date()) +"
"
+this.name+": "+msg);
break;
}
}
}else {
//群聊
for(Channel other:all) {
if (other == this) {//本身
continue;
}
if (!isSys) {
other.send("
"+new SimpleDateFormat("YYYY-MM-dd HH:MM:ss SSS").format(new Date()) +"
"
+this.name+": "+msg);
}else {
other.send("
"+new SimpleDateFormat("YYYY-MM-dd HH:MM:ss SSS").format(new Date()) +"
"
+"系统消息"+": "+msg);
}
}
}
}
//释放资源
private void release () {
this.isRunning = false;
Utils.close(dis,dos,client);
//退出
all.remove(this);
sendOthers(this.name+"离开了!", true);
}
@Override
public void run() {
while(isRunning) {
String msg = receive();
if (!msg.equals("")) {
//send(msg);
sendOthers(msg,false);
}
}
}
}
}
客服端LoginMultiServer.class
package com.gqz.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 在线聊天
* 封装 实现多用户聊天
*
* @ClassName: Client
* @Description: TODO(这里用一句话描述这个类的作用)
* @author ganquanzhong
* @date 2019年7月16日 下午3:01:07
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print(" 进入群聊系统 输入您的用户名称:");
String name = br.readLine();
System.out.println("=====client="+name+"====");
// 1、建立面向连接:使用Socket创建客户端 (服务器地址和端口)
Socket client = new Socket("localhost", 9999);
//客户端发送消息
new Thread(new Send(client,name)).start();
//客户端接收消息
new Thread(new Receive(client)).start();
}
}
Send.class
package com.gqz.chat;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 使用多线程封装:发送端
* 1、发送消息
* 2、从控制台获取消息
* 3、释放资源
* 4、重写run
*
* @ClassName: Send
* @Description: TODO(这里用一句话描述这个类的作用)
* @author ganquanzhong
* @date 2019年7月16日 下午5:09:16
*/
public class Send implements Runnable {
private BufferedReader console;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
public Send(Socket client,String name) {
this.client = client;
isRunning=true;
this.name= name;
console = new BufferedReader(new InputStreamReader(System.in));
try {
dos = new DataOutputStream(client.getOutputStream());
//发送名称
send(name);
} catch (IOException e) {
System.out.println("====初始化错误====");
this.release();
e.printStackTrace();
}
}
// 发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println("--client-发送消息错误----");
release();
e.printStackTrace();
}
}
// 从控制台获取消息
private String getFromConsole() {
try {
return console.readLine();
} catch (IOException e) {
System.out.println("=====console error=====");
e.printStackTrace();
}
return "";
}
// 释放资源1
private void release() {
this.isRunning = false;
Utils.close(dos, client);
}
@Override
public void run() {
while (isRunning) {
String msg = getFromConsole();
if (!msg.equals("")) {
send(msg);
}
}
}
}
Receive.class
package com.gqz.chat;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* 使用多线程封装:接收端
* 1、接收消息
* 2、释放资源
* 3、重写run
*
* @ClassName: Receive
* @Description: TODO(这里用一句话描述这个类的作用)
* @author ganquanzhong
* @date 2019年7月16日 下午5:08:34
*/
public class Receive implements Runnable {
private DataInputStream dis;
private Socket client;
private boolean isRunning;
public Receive(Socket client) {
this.client = client;
isRunning=true;
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
System.out.println("====client 接收消息 初始化错误=====");
release();
e.printStackTrace();
}
}
// 接收消息
private String receive() {
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
System.out.println("---client 接收消息错误----");
release();
e.printStackTrace();
}
return msg;
}
// 释放资源1
private void release() {
this.isRunning = false;
Utils.close(dis, client);
}
@Override
public void run() {
while(isRunning) {
String msg = receive();
if (! msg.equals("")) {
System.out.println(msg);
}
}
}
}
Utils.class
package com.gqz.chat;
import java.io.Closeable;
/**
* 工具类
*
* @ClassName: Utils
* @Description: TODO(这里用一句话描述这个类的作用)
* @author ganquanzhong
* @date 2019年7月16日 下午4:25:10
*/
public class Utils {
/*
* 释放资源
*/
public static void close(Closeable... targets) {
for(Closeable target:targets) {
try {
if (null != target) {
target.close();
}
}catch(Exception e) {
}
}
}
}