java实现socket连接⽅法封装_Java基础系列:Socket编程俗世游⼦:专注技术研究的程序猿
说到前⾯的话
没有实战案例的理论基础都是在耍流氓,所以今天主要是想通过这⾥的案例能够让⼤家加深对之前的理解
本节我们会⼀步步实现⼀个点对点聊天⼩程序
Java中的Socket实现
InetAddress
InetAddress是Java对IP地址的封装,这个类是⼀个基础类,下⾯的ServerSocket和DatagramSocket都离不开这个类
InetAddress⽆法通过new的⽅式来初始化,只能提供过其提供的静态⽅法来调⽤:
// 获取本地地址
InetAddress localHost = LocalHost();
这⾥是InetAddress的⼀些⽅法:
// 主机名:DESKTOP-ATG4KKE
System.out.println("主机名:" + HostName());
// IP地址:192.168.87.1
System.out.println("IP地址:" + HostAddress());
/
/ 是否正常:true
System.out.println("是否正常:" + localHost.isReachable(5000));
这⾥是我测试时的输出,
关于isReachable()的⽅法,⽤来检测该地址是否可以访问,由此我们可以做⼀些健康检查操作,⽐如:
// 通过主机IP或者域名来得到InetAddress对象
InetAddress inetAddress = ByName("192.168.87.139");
System.out.println("是否正常:" + inetAddress.isReachable(5000));
在5s之内尽最⼤可能尝试连接到主机,如果没有就认为主机不可⽤,这⾥受限于防⽕墙和服务器配置
当然,做健康检查这种⽅法还是low了点,⽣产环境中肯定不会这么⼲
PS: ⽣产环境的⽹络操作不会使⽤到这节⾥的东西,⼤部分情况下采⽤的都是Netty
ServerSocket
ServerSocket是服务端套接字,是基于TCP/IP协议下的实现
初始化
通常我们这样来构建:
ServerSocket serverSocket = new ServerSocket(9999);java replace方法
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(9999));
这样就完成了服务端的初始化,并且将端⼝9999绑定起来
等待连接
如果客户端想要和ServerSocket建⽴连接,我们需要这么做
for(;;) {
Socket socket = serverSocket.accpet();
/
/ Socket[addr=/0:0:0:0:0:0:0:1,port=62445,localport=9999]
System.out.println(socket);
}
accpet()是侦听与ServerSocket建⽴的连接,这个⽅法是⼀个阻塞⽅法,会⼀直等待连接接⼊进来
如果有连接接⼊进来,我们可以通过返回值来得到当前接⼊进来的Socket
通信
在⽹络中传递数据其实也是按照IO流的⽅式进⾏传递的,但是我们只能获取到字节流:
InputStream inputStream = InputStream();
OutputStream outputStream = OutputStream();
InputStream读取数据,OutputStream写出数据,这些基本操作我们在之前的IO流中都介绍过,这⾥就不再多说这⾥我们为了能够提⾼效率,可以采⽤包装流或者处理流来处理,这前⾯也介绍过了
完整⼩例⼦
其实到这⾥,ServerSocket的关键介绍也就完了,下⾯我们来做⼀个⼩例⼦:
当有客户端连接进来之后,给客户端返回:Hello World
public class _ServerSocket {
// ⽤来存储请求客户端和Socket之间的对应关系
static Map MAP = new HashMap<>();
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(9999));
for (; ; ) {
String token = UUID.randomUUID().toString().replace("-", "").toLowerCase();
Socket socket = serverSocket.accept();
// 对应
MAP.put(token, socket);
outHtml(socket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void outHtml(Socket socket) {
OutputStream outputStream = null;
try {
outputStream = OutputStream();
outputStream.write(("HTTP/1.1 200 OK\n\nHello World").getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
HTTP/1.1 200 OK\n\nHello World\n
这是HTTP协议下返回类型,前⾯是Response固定格式,Hello World是真正返回的内容,这样我们的ServerSocket就能够通过浏览器来访问了
Socket
Socket属于客户端套接字,只有先和服务端套接字建⽴连接才能做其他的操作,Socket的使⽤⽅式⾮
常简单建⽴连接
Socket socket = new Socket("127.0.0.1", 9999);
// 验证是否连接成功
if (socket.isConnected()) {
System.out.println("到服务端连接成功");
}
这是其中⼀种构造⽅法,更多情况下是采⽤这种⽅式
和服务端的连接建⽴成功之后,后续的操作就和ServerSocket的通信步骤⼀样了,这⾥就不再多废话了
下⾯⽤⼀个完整的例⼦来巩固⼀下
案例:TCP点对点聊天
服务端
public class Server {
/**
* 将客户端标识和socket关联起来
*/
private static final Map SOCKET_MAP = new HashMap<>();
/**
* 反向关联,⽤来获取标识
*/
private static final Map SOCKET_TOKEN_MAP = new HashMap<>();
public static void main(String[] args) throws IOException {
/**
* 开启ServerSocket并监听9999端⼝
*/
ServerSocket serverSocket = new ServerSocket(9999);
for (;;) {
/**
* 等待客户端连接
*/
Socket socket = serverSocket.accept();
/**
* IO读取是阻塞式⽅法,所以需要开启新线程,这⾥可以优化成线程池
*/
new Thread(() -> {
try {
saveToMap(socket);
getClientMsg(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* 绑定SOCKET
*/
private static void saveToMap(Socket socket) throws IOException {
String token = StringUtil.uuid();
SOCKET_MAP.put(token, socket);
SOCKET_TOKEN_MAP.put(socket, token);
System.out.println("---客户端连接成功,编号:" + token);
System.out.println("当前⽤户:" + SOCKET_MAP.size());
/**
* 因为没有登录,所以这⾥要告知客户端⾃⼰的标识
*/
send(token, token, token);
}
/**
* 获取客户端发送过来的消息,并发送出指定指定的客户端
*/
private static void getClientMsg(Socket socket) throws IOException {
BufferedReader reader = new BufferedReader(new InputStream())); String line = "";