TCP连接流程
整个的连接流程如图所示,需要注意的是服务器端创建了两个socket,一个用于监听,一个用于接受具体的连接,而本文所主要采用的多线程客户-服务器socket通信,主要是将连接成功的socket交给不同的线程去处理,来执行不同的数据传输任务(图中蓝色部分),从而服务器可以继续监听来自其他客户端的连接不被阻塞住。
代码实战
1. 目录结构
2. 代码详解
客户端代码 Client.java
package myTCP;
import java.net.Socket;
import java.net.UnknownHostException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class Client {
public static void main(String[] args) {
try{
// 创建 socket 与 localhost:8888(服务器) 建立连接
Socket socket = new Socket("localhost", 8888);
//注意以上连接方式和流程图不符
//我们没有绑定客户端的本地端口,如果我们想绑定可以按照如下方式:
//Socket socket=new Socket();
//socket.bind(new InetSocketAddress(3000)); // 绑定了本地的3000端口
//socket.connect(new InetSocketAddress("localhost", 8888));
// 获取socket输出流(这里的输出是相对与客户端的也就是会输出到服务器)
OutputStream outStream = socket.getOutputStream();
// PrintWriter 以便以我们可以传入更多类型的数据
PrintWriter printWriter = new PrintWriter(outStream);
// 要传输的内容
printWriter.println("userName: zxd passWord: zxd");
// 刷新缓冲区, 以免缓冲区残留数据
printWriter.flush();
// 关闭输出流,代表客户端向服务器传输数据结束
socket.shutdownOutput();
// 获取socket输入流(这里的输出是相对与客户端的也就是会会接受服务器的输出)
InputStream is = socket.getInputStream();
// InputStreamReader 使得我们可以按不同编码方式读取
InputStreamReader isr = new InputStreamReader(is);
// 读缓冲区
BufferedReader br = new BufferedReader(isr);
// 定义接收数据格式
String data = null;
// 按行接收并打印结果
while ((data = br.readLine()) != null) {
System.out.println("服务器返回的数据为:"+data);
}
// 收发数据均已经结束 关闭socket
socket.close();
// 以下是必须进行捕获的异常
} catch (UnknownHostException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端代码 Server.java
package myTCP;
import java.net.ServerSocket;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
// 定义监听socket 监听8888端口
ServerSocket serverSocket = new ServerSocket(8888);
// 定义连接socket
Socket socket = null;
// 记录客户端的连接次数
int count = 0;
// 此时服务器已经准备好 打印log
System.out.println("Sever Start!!");
// 循环监听
while (true) {
// 连接socket建立成功,
socket = serverSocket.accept();
// 将具体的数据传输任务丢给连接处理线程
Thread thread = new Thread(new ServerThread(socket));
// 线程开始运行
thread.start();
// 连接数加一
count++;
// 实时打印log 显示连接数
System.out.println("The num of connection :" + count);
// 获取连接地址
InetAddress address = socket.getInetAddress();
// 实时打印log 显示当前连接的客户端地址
System.out.println("The address of client now:" +
address.getHostAddress());
}
// 服务器的连接不用关闭
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端多线程代码 ServerThread.java
package myTCP;
import java.net.Socket;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.IOException;
// 线程类实现runnable接口
public class ServerThread implements Runnable{
// 对应连接socket
Socket socket = null;
// 构造函数初始化连接socket
public ServerThread(Socket socket){
this.socket = socket;
}
public void run(){
// 与客户端一样定义一系列输入输出流和缓冲区
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try{
// 获取客户端的输入
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
// 定义接收数据
String data = null;
// 按行接收,并返回数据给客户端
while((data = br.readLine()) != null) {
System.out.println("The client seed data:" + data);
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.println("Access tcpServer Successfully!");
pw.flush();
}
// 必须进行的异常捕获
} catch (IOException e) {
e.printStackTrace();
} finally {
// 按顺序关闭缓冲区和流(先开后关)
try {
if (pw != null) {
pw.close();
}
if (os != null) {
os.close();
}
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (is != null) {
is.close();
}
if (socket!= null) {
socket.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
3. 结果展示
-
先运行Server.java 文件
-
在运行客户端文件
-
此时服务端会打印log
-
保持服务端开启,再次运行客户端程序