系列文章目录
Java 网络编程 01 —— Socket TCP通信
Java 网络编程 02 —— Socket UDP通信
Java 网络编程 03 —— URL下载资源
Java Socket TCP通信
TCP实现聊天
服务器:
- 建立服务器的端口。利用ServerSocket类。
- 等待用户的连接 。利用serversocket的accept方法返回一个客户端的socket。
- 接受用户消息。利用返回的客户端的socket获取输入流。
package lessen01;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
//服务器
public class TCPServerDemo01 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
int count = 1;
try {
//1. 有一个地址和端口号
serverSocket = new ServerSocket(9999);//自定义一个端口
//实现一直传输,直到客户端输入end
while(true){
//2. 等待客户端连接
socket = serverSocket.accept();//获取客户端的socket
//3. 读取客户端的消息
is = socket.getInputStream();//获取客户端socket的输入流
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer))!=-1){
baos.write(buffer, 0, len);
}
System.out.println(baos.toString()+"。第"+(count++)+"次连接。");
if(baos.toString().equals("end"))
break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭所有流
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端:
- 利用InetAddress设置好服务器IP地址。
- 在服务器已建立好的前提下,连接服务器。利用Socket类连接服务器。
- 发送消息。利用Socket类获取输出流。
package lessen01;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
//客户端
public class TCPClientDemo01 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
Scanner scanner = new Scanner(System.in);
String string = null;
//实现一直传输,直到键盘输入end
while(true){
try {
//1. 知道服务器地址,端口号
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
int port = 9999;
//2. 创建一个Socket连接
socket = new Socket(serverIP, port);
//3. 发送消息 IO流
os = socket.getOutputStream();
string = scanner.nextLine();
os.write(string.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
if(string.equals("end"))
break;
}
}
}
TCP实现文件上传
客户端:
- 创建Socket连接。
- 从Socket获取输出流。
- 创建文件输入流。
- 从文件输入流中读取文件,并输出到服务器。
- 通知服务器,已发送完毕。
- 确定服务器接受完毕。
- 关闭资源。
package lessen01;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClientDemo02 {
public static void main(String[] args) throws Exception {
//1. 创建一个Socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
//2. 从Socket获取输出流
OutputStream os = socket.getOutputStream();
//3. 创建文件输入流
FileInputStream fis = new FileInputStream(new File("zhang.png"));
//4. 从文件输入流中读取文件,并输出到服务器
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer))!= -1){
os.write(buffer, 0, len);//将字节数组中内容全部输出
}
//5. 通知服务器,已发送完毕。这里如果不通知,则程序会陷入阻塞。下面解释原因。
socket.shutdownOutput();
//6. 确定服务器接受完毕
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while((len2 = is.read(buffer2))!=-1){
baos.write(buffer2, 0, len2);
}
System.out.println(baos.toString());
//7. 关闭资源
baos.close();
is.close();
fis.close();
os.close();
socket.close();
}
}
服务器:
- 创建一个ServerSocket连接。
- 监听客户端连接。
- 读取客户端数据。
- 创建文件输出流。
- 通知客户端,已接受完毕。
- 关闭资源。
package lessen01;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerDemo02 {
public static void main(String[] args) throws Exception {
//1. 创建一个ServerSocket连接
ServerSocket serverSocket = new ServerSocket(9999);
//2. 监听客户端连接
Socket socket = serverSocket.accept();//阻塞式监听,会一直等待连接
//3. 读取客户端数据
InputStream is = socket.getInputStream();
//4. 创建文件输出流
FileOutputStream fos = new FileOutputStream(new File("./receive.png"));
byte[] buffer = new byte[1024];
int len;
while((len=is.read(buffer))!=-1){
fos.write(buffer, 0, len);
}
//5. 通知客户端,已接受完毕
OutputStream os = socket.getOutputStream();
os.write("服务器已接受数据完毕".getBytes());
//6. 关闭资源
os.close();
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
上述客户端部分第5步, socket.shutdownOutput();是告诉服务器客户端数据已经发送完毕,并关闭输出流【此时Socket仍然连接,只是输出流关闭了】。
如果省略这一步,后续客户端会一直卡在确定服务器接受数据完毕的read()位置,等待服务器回复。而服务器因为不知道客户端是否发送完毕,也会一直卡在第4步的read()位置。
之前TCP实现聊天是因为调用close()方法,关闭了输入输出流,服务器客户端双知道,而这次在close()前面还有一次通知操作,因此需手动调用shutdownOutput(),来断开客户端的输出流,然后服务器继续执行第五步反馈客户端。