前言
在开发网络应用程序的时候,会遇到Socket这个概念,一个应用程序通过一个Socket来建立一个远程连接,Socket内部通过Tcp/Ip协议把数据传输到网络。
┌───────────┐ ┌───────────┐
│Application│ │Application│
├───────────┤ ├───────────┤
│ Socket │ │ Socket │
├───────────┤ ├───────────┤
│ TCP │ │ TCP │
├───────────┤ ┌──────┐ ┌──────┐ ├───────────┤
│ IP │<────>│Router│<─────>│Router│<────>│ IP │
└───────────┘ └──────┘ └──────┘ └───────────┘
一个Socket就是由IP地址和端口号(范围是0~65535)组成,可以把Socket简单理解为IP地址加端口号。端口号总是由操作系统分配,它是一个数字,其中,小于1024的端口属于特权端口,需要管理员权限,大于1024的端口可以由任意用户的应用程序打开对于服务器端来说,它的Socket是 指定的IP地址和制动的端口号;对于客户端来说,它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。
服务器端
要使用Socket编程,我们首先要编写服务器端程序。Java标准库提供了ServerSocket来实现对指定IP和指定端口的监听。
ServerSocket server;
try {
server = new ServerSocket(8888);
while (true) {
Socket client = server.accept();
InetAddress clientNetAddress = client.getInetAddress();
System.out.println("客户端" + clientNetAddress.getHostAddress() + "开始连接.......");
String imageName = clientNetAddress.getHostAddress().replaceAll("\\.", "-") + ".jpg";
try (InputStream in = client.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("C:\\Users\\admin\\Desktop\\" + imageName));) {
byte[] buff = new byte[1024];
int len = -1;
while ((len = bis.read(buff)) != -1) {
bos.write(buff, 0, len);
}
System.out.println("图片读取完毕!");
try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))){
writer.write("upload success!!!");
writer.newLine();
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
服务器端在指定端口8888监听 ,这里 没有指定Ip地址,表示计算机的所有网络接口上进行监听。ServerSocket监听成功,我们就使用一个无限循环来处理客户端的连接,注意到server.accept()表示当 有新的客户端连接进来后,就返回一个Socket实例,这个Socket实例就是用来和刚连接的客户端进行通信的。如果没有客户端连接进来,accept()方法会阻塞并一直 等待。如果有多个客户端同时连接进来,ServerSocket会把连接扔到队列里,然后一个一个处理。对于Java程序而言,只需要通过循环不断调用accept()就可以获取新的连接。
客户端
try (Socket client = new Socket("192.168.10.21", 8888);
OutputStream out = client.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("C:\\Users\\admin\\Desktop\\aaa\\1657264617271.jpg"))) {
byte[] buff = new byte[1024];
int len = -1;
while ((len = bis.read(buff)) != -1) {
out.write(buff);
}
client.shutdownOutput();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()))) {
String reply = reader.readLine();
System.out.println("服务器反馈:" + reply);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
客户端程序通过连接到服务器端,如果连接成功,将返回一个Socket实例,用于后续通信。
Socket流
当Socket连接创建成功后,无论是服务器端,还是客户端,我们都使用Socket实例进行网络通信。因为TCP是一种基于流的协议,因此,Java标准库使用InputStream和OutPutStream来封装Socket的数据流,这样我们使用Socket的流,和普通的IO流类似。
总结
使用Java进行TCP编程时,需要使用Socket模型;
服务器用ServerSocket监听指定接口;
客户端使用Socket(InetAddress,port)连接服务器;
服务器端用accept()接受连接并返回Socket实例;
双方通过Socket打开InputStream/OutputStream读写数据;