目录
什么是TCP?
TCP特点
TCP编程核心类
TCP编程
1、基本通信
2、文件类型通信
3、多客户端同时通信
什么是TCP?
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP特点
- 面向连接,通信前由客户端主动发起连接请求
- 低效
- 可靠,客户端与服务端必须建立连接后才能通讯
- 基于请求-响应模式
- 第一次主动发起通讯的程序被称作客户端(Client),第一次通讯中等待连接的程序称为服务端(Server)
- 在JAVA中使用IO流实现数据传输
TCP编程核心类
ServerSocket:这个类实现了服务器套接字。 服务器套接字等待通过网络进入的请求。 它根据该请求执行一些操作,然后可能将结果返回给请求者
Socket:该类实现客户端套接字。主动发起连接请求
TCP编程
TCP编程的主要步骤可以概括为以下几步
服务端
- 创建ServerSocket(int port)对象
- 在ServerSocket上监听客户端连接请求
- 接收并处理消息
- 返回处理结果给客户端
- 释放资源
客户端
- 创建Socket(String host, int port)对象
- 建立连接,java中创建Socket对象时就会连接
- 发送请求信息
- 接收响应信息
- 释放资源
1、基本通信
服务端
public class TcpServer {
public static void main(String[] args) throws IOException {
// 1、指定端口、使用ServerSocket创建服务端
ServerSocket server = new ServerSocket(8888);
// 2、阻塞式等待客户端连接
Socket client = server.accept(); // 这里的client就是发送连接请求的client
System.out.println("成功建立连接");
// 3、获取客户端传递的数据
DataInputStream dis = new DataInputStream(client.getInputStream());
System.out.println(dis.readUTF());
// 4、响应客户端
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("接收到消息");
dos.flush();
// 5、释放资源
dis.close();
client.close();
server.close();
}
}
客户端
public class TcpClient {
public static void main(String[] args) throws IOException {
// 1、使用Socket创建客户端,并指定要连接的服务端地址和端口
Socket client = new Socket("localhost", 8888);
// 2、传递数据
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("hello world");
// 3、接收响应信息
DataInputStream dis = new DataInputStream(client.getInputStream());
System.out.println(dis.readUTF());
// 4、释放资源
dos.close();
client.close();
}
}
2、文件类型通信
与UDP修改的思路一样,客户端只需要用文件输入流读取文件,并使用输出流发送,服务端接收数据,并用文件输出流写到本地即可。
服务端
// 1、指定端口、使用ServerSocket创建服务端
// 2、阻塞式等待客户端连接
// 3、获取客户端传递的数据
InputStream is = new BufferedInputStream(client.getInputStream());
OutputStream os = new BufferedOutputStream(new FileOutputStream("src/test/tcp.png"));
byte[] flush = new byte[1024];
int len = -1;
while ((len = is.read(flush)) != -1) {
os.write(flush, 0, len);
}
os.flush();
// 4、响应客户端
// 5、释放资源
客户端
// 1、使用Socket创建客户端,并指定要连接的服务端地址和端口
// 2、传递数据,上传文件
InputStream is = new BufferedInputStream(new FileInputStream("src/test/1.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、接收响应信息
// 4、释放资源
3、多客户端同时通信
要多个客户端与服务器建立连接,非常简单,服务端等待连接的代码server.accept()上加一个while循环即可
while (true) {
Socket client = server.accept();
// 后续操作
}
但是,这样做,虽然能够建立连接,但是后台的客户端必须等待前一个客户端执行完后续操作后才能进行通信操作,所以为了同时进行通信,我们需要使用多线程,将服务端接受和发送信息的过程封装起来。在下面的例子中,我们使用TCP模拟一个用户登录的过程。
服务端
public class TcpServer {
public static void main(String[] args) throws IOException {
//1、指定端口、使用ServerSocket创建服务端
ServerSocket server = new ServerSocket(8888);
boolean isRunning = true;
//2、阻塞式等待客户端连接
while (isRunning) {
Socket client = server.accept();
System.out.println("一个客户端成功建立连接");
// 建立连接后就单独开启一个线程为当前的客户端服务
new Thread(new Channel(client)).start();
}
//4、释放资源
server.close();
}
}
/**
* 封装通信过程,包含了客户端的输入输出流,称为一个管道
*/
class Channel implements Runnable {
private Socket client;
private DataInputStream dis;
private DataOutputStream dos;
Channel(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//3、获取客户端传递的数据, 参数格式是user=xxx&pwd=xxx
String datas = null;
try {
datas = dis.readUTF();
String user = "";
String pwd = "";
String[] arr = datas.split("&");
for (String msg : arr) {
String[] info = msg.split("=");
if (info[0].equals("user")) {
System.out.println("用户名是:" + info[1]);
user = info[1];
} else {
System.out.println("密码是:" + info[1]);
pwd = info[1];
}
}
//4、响应
if ("icydate".equals(user) && "123".equals(pwd)) {
dos.writeUTF("登陆成功");
} else {
dos.writeUTF("登陆失败");
}
dis.close();
dos.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端
public class TcpClient {
public static void main(String[] args) throws IOException {
// 接收控制台输入的用户民和密码
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入用户名:");
String user = br.readLine();
System.out.print("请输入密码:");
String pwd = br.readLine();
// 1、使用Socket创建客户端,并指定要连接的服务端地址和端口
Socket client = new Socket("localhost", 8888);
// 2、传递数据
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("user=" + user + "&pwd=" + pwd);
dos.flush();
// 3、接收响应
DataInputStream dis = new DataInputStream(client.getInputStream());
System.out.println(dis.readUTF());
// 4、释放资源
dos.close();
client.close();
}
}