目录
1. ServerSocket API(给服务器端使用的类)
2. Socket API(既给服务器使用,也给客户端使用)
TCP流套接字编程
1. ServerSocket API(给服务器端使用的类)
ServerSocket 是创建TCP服务端Socket的API。
构造方法
方法签名 | 说明 |
---|---|
ServerSocket(int port) | 创建一个服务端流套接字Socket,并绑定到指定端口 |
方法
方法签名 | 说明 |
---|---|
Socket accept() | 开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于改Socket建立与客户端的连接,否则阻塞等待(接受客户端的连接) |
void close() | 关闭此套接字 |
2. Socket API(既给服务器使用,也给客户端使用)
Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的
构造方法
方法签名 | 说明 |
---|---|
Socket(String host, int port) | 创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的 进程建立连接(尝试和指定的服务器建立连接) |
方法
方法签名 | 说明 |
---|---|
InetAddress getInetAddress() | 返回套接字所连接的地址(返回套接字获取到对方的IP地址和端口) |
InputStream getInputStream() | 返回此套接字的输入流(通过Socket可以获取到两个流对象,分别用来读和写) |
OutputStream getOutputStream() | 返回此套接字的输出流 |
3. 写TCP回显—服务器
1.先写一个ServerSocket对象
private ServerSocket listenSocket = null;
2.下面写Tcp服务器构造方法
public TcpEchoServer (int port) throws IOException { listenSocket = new ServerSocket(port); }3.写一个启动服务器的方法start(),在start中写上while循环来执行
a)调用accept来接收客户端的连接
b)再处理这个连接,这里写一个processConnection()方法来处理
public void start () throws IOException { System.out.println( "服务器启动!"); while( true) { //1. 先调用 accept 来接受客户端的连接 Socket clientSocket = listenSocket.accept(); //2. 再处理这个连接 processConnection(clientSocket); } }4.下面来写这个processConnection()方法,处理连接客户端连接
方法中写try(这里写上InputStream(读)和OutPutStream(写)对象,写在try中帮助资源回收) {这里写上写具体处理逻辑步骤}
注意最后必须要写上finally来close关闭clientSocket
private void processConnection (Socket clientSocket) throws IOException { System.out.printf( "[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort()); //接下来处理客户端请求 try( InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream()) { while( true) { } } finally { //这里要关闭socket,是因为 //socket也是一个文件,一个进程能够同时打开的文件个数有上限(PCB文件描述符表,不是无限的 clientSocket.close(); } }a)读取请求并解析
Scanner scanner = new Scanner(inputStream); if(!scanner.hasNext()) { //读完了,连接可以断开了 System.out.printf( "[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort()); break; } String request = scanner.next();b)根据请求计算响应(这里写一个process方法)
String response = process(request);
private String process (String request) { return request; }c)响应写回到客户端
PrintWriter printWriter = new PrintWriter(outputStream); printWriter.println(response); //刷新缓冲区确保数据确实通过网卡发送出去了 printWriter.flush();d)将发送的信息显示到服务器界面上
System.out.printf( "[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request,response);5.最后再写上mian方法来执行服务器
public static void main (String[] args) throws IOException { TcpEchoServer tcpEchoServer = new TcpEchoServer( 9090); tcpEchoServer.start(); }
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.io.OutputStream;
-
import java.io.PrintWriter;
-
import java.net.ServerSocket;
-
import java.net.Socket;
-
import java.util.Scanner;
-
import java.util.concurrent.Semaphore;
-
-
/**
-
* Created with IntelliJ IDEA.
-
* Description:
-
* User: 28463
-
* Date: 2022—10—14
-
* Time: 17:05
-
*/
-
public
class
TcpEchoServer {
-
//代码中会设计到多个 socket 对象,使用不同的名字来区分
-
private
ServerSocket
listenSocket
=
null;
-
-
public
TcpEchoServer
(int port)
throws IOException {
-
listenSocket =
new
ServerSocket(port);
-
}
-
-
public
void
start
()
throws IOException {
-
System.out.println(
"服务器启动!");
-
while(
true) {
-
//1. 先调用 accept 来接受客户端的连接
-
Socket
clientSocket
= listenSocket.accept();
-
//2. 再处理这个连接
-
processConnection(clientSocket);
-
}
-
}
-
-
private
void
processConnection
(Socket clientSocket)
throws IOException {
-
System.out.printf(
"[%s:%d] 客户端上线!\n",
-
clientSocket.getInetAddress().toString(),
-
clientSocket.getPort());
-
-
//接下来处理客户端请求
-
try(
InputStream
inputStream
= clientSocket.getInputStream();
-
OutputStream
outputStream
= clientSocket.getOutputStream()) {
-
while(
true) {
-
//1.读取请求并解析
-
Scanner
scanner
=
new
Scanner(inputStream);
-
if(!scanner.hasNext()) {
-
//读完了,连接可以断开了
-
System.out.printf(
"[%s:%d] 客户端下线!\n",
-
clientSocket.getInetAddress().toString(),
-
clientSocket.getPort());
-
break;
-
}
-
String
request
= scanner.next();
-
//2.根据请求计算响应
-
String
response
= process(request);
-
//3.响应写回到客户端
-
PrintWriter
printWriter
=
new
PrintWriter(outputStream);
-
printWriter.println(response);
-
//刷新缓冲区确保数据确实通过网卡发送出去了
-
printWriter.flush();
-
-
System.out.printf(
"[%s:%d] req: %s; resp: %s\n",
-
clientSocket.getInetAddress().toString(),
-
clientSocket.getPort(),
-
request,response);
-
}
-
}
catch (IOException e) {
-
e.printStackTrace();
-
}
finally {
-
clientSocket.close();
-
}
-
}
-
-
private String
process
(String request) {
-
return request;
-
}
-
-
public
static
void
main
(String[] args)
throws IOException {
-
TcpEchoServer
tcpEchoServer
=
new
TcpEchoServer(
9090);
-
tcpEchoServer.start();
-
}
-
}
下面思考为啥代码最后finally要执行clientSocket的close,而前面的listenSocket以及UDP程序中的socekt为啥就没close?
但是上面的代码有个问题,只能处理一个客户端的请求,(本质上第二个客户端的消息是发出去了,但服务器此时还在执行第一个客户端的请求,只要从第一个客户端这里出来,服务器就会立刻执行第二个客户端的消息)
我们希望的是既能够快速重复的调用到accept(也就是连接多个客户端),又能够循环的处理客户端的请求。所以就需要使用到多线程了
那么为什么前面UDP就不需要考虑这个问题,而TCP需要考虑
UDP是无连接,客户端直接发消息就行(不必专注于处理某一个客户端)
TCP建立连接之后,要处理客户端的多次请求,才导致无法快速的调用到accept(长连接)(主要原因)
如果TCP每个连接只处理一个客户端的请求,也能够保证快速调用到accept(短连接)
下面使用多线程,给每个客户端连上来的都分配一个新的线程负责处理请求
但是直接这样使用多线程,如果循环多次,对应就会创建很多线程,等线程执行完,又会消毁很多的线程,所以更好的方法就是使用线程池
4. 使用线程池后的TCP服务器代码(最终)
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.io.OutputStream;
-
import java.io.PrintWriter;
-
import java.net.ServerSocket;
-
import java.net.Socket;
-
import java.util.Scanner;
-
import java.util.concurrent.Executor;
-
import java.util.concurrent.ExecutorService;
-
import java.util.concurrent.Executors;
-
import java.util.concurrent.Semaphore;
-
-
/**
-
* Created with IntelliJ IDEA.
-
* Description:
-
* User: 28463
-
* Date: 2022—10—14
-
* Time: 17:05
-
*/
-
public
class
TcpEchoServer {
-
//代码中会设计到多个 socket 对象,使用不同的名字来区分
-
private
ServerSocket
listenSocket
=
null;
-
-
public
TcpEchoServer
(int port)
throws IOException {
-
listenSocket =
new
ServerSocket(port);
-
}
-
-
public
void
start
()
throws IOException {
-
System.out.println(
"服务器启动!");
-
ExecutorService
service
= Executors.newCachedThreadPool();
-
while(
true) {
-
//1. 先调用 accept 来接受客户端的连接
-
Socket
clientSocket
= listenSocket.accept();
-
//2. 再处理这个连接,这里应该要使用多线程,每个客户端连上来都分配一个新的线程负责处理
-
service.submit(
new
Runnable() {
-
@Override
-
public
void
run
() {
-
try {
-
processConnection(clientSocket);
-
}
catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
});
-
}
-
}
-
-
private
void
processConnection
(Socket clientSocket)
throws IOException {
-
System.out.printf(
"[%s:%d] 客户端上线!\n",
-
clientSocket.getInetAddress().toString(),
-
clientSocket.getPort());
-
-
//接下来处理客户端请求
-
try(
InputStream
inputStream
= clientSocket.getInputStream();
-
OutputStream
outputStream
= clientSocket.getOutputStream()) {
-
while(
true) {
-
//1.读取请求并解析
-
Scanner
scanner
=
new
Scanner(inputStream);
-
if(!scanner.hasNext()) {
-
//读完了,连接可以断开了
-
System.out.printf(
"[%s:%d] 客户端下线!\n",
-
clientSocket.getInetAddress().toString(),
-
clientSocket.getPort());
-
break;
-
}
-
String
request
= scanner.next();
-
//2.根据请求计算响应
-
String
response
= process(request);
-
//3.响应写回到客户端
-
PrintWriter
printWriter
=
new
PrintWriter(outputStream);
-
printWriter.println(response);
-
//刷新缓冲区确保数据确实通过网卡发送出去了
-
printWriter.flush();
-
-
System.out.printf(
"[%s:%d] req: %s; resp: %s\n",
-
clientSocket.getInetAddress().toString(),
-
clientSocket.getPort(),
-
request,response);
-
}
-
}
catch (IOException e) {
-
e.printStackTrace();
-
}
finally {
-
//这里要关闭socket,是因为
-
//socket也是一个文件,一个进程能够同时打开的文件个数有上限(PCB文件描述符表,不是无限的
-
clientSocket.close();
-
}
-
}
-
-
private String
process
(String request) {
-
return request;
-
}
-
-
public
static
void
main
(String[] args)
throws IOException {
-
TcpEchoServer
tcpEchoServer
=
new
TcpEchoServer(
9090);
-
tcpEchoServer.start();
-
}
-
}
5. 写回显-客户端
1. 先写一个Socket对象,客户端用Socket来建立连接
private Socket socket = null;
2.下面写Tcp客户端构造(和Udp区别较大,有连接和无连接的区别)
public TcpEchoClient (String serverIP, int serverPort) throws IOException { //和服务器建立连接,就需要知道服务器在哪 socket = new Socket(serverIP,serverPort); }3.写客户端执行方法start(),给start里面放try,try中执行的就是while,来让客户端循环输入,还要在try后面括号中写上InputStream(读)和OutputStream(写)的对象(写在括号中中try会自动帮助,关掉资源)
public void start () throws IOException { Scanner scan = new Scanner(System.in); try( InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) { while( true) { } } }while中写 a)从控制台读取数据,构造一个请求
System.out.println( "-> "); String request = scan.next();b)发送请求给服务器(PrintWriter)
PrintWriter printWriter = new PrintWriter(outputStream); printWriter.println(request); //这个 flush 不要忘记,否则可能导致请求没有真发出去 printWriter.flush();c)从服务器读取响应
Scanner respScanner = new Scanner(inputStream); String response = respScanner.next();d)把响应显示到界面上
System.out.println(response);
4.最后再写上mian方法来执行客户端
public static void main (String[] args) throws IOException { TcpEchoClient tcpEchoClient = new TcpEchoClient( "127.0.0.1", 9090); tcpEchoClient.start();
Udp和Tcp构造的区别,有连接和无连接
6. TCP回显—客户端代码
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.io.OutputStream;
-
import java.io.PrintWriter;
-
import java.net.Socket;
-
import java.util.Scanner;
-
-
/**
-
* Created with IntelliJ IDEA.
-
* Description:
-
* User: 28463
-
* Date: 2022—10—14
-
* Time: 17:06
-
*/
-
public
class
TcpEchoClient {
-
//客户端需要使用这个 Socket 对象来建立连接
-
private
Socket
socket
=
null;
-
-
public
TcpEchoClient
(String serverIP, int serverPort)
throws IOException {
-
//和服务器建立连接,就需要知道服务器在哪
-
socket =
new
Socket(serverIP,serverPort);
-
}
-
-
public
void
start
()
throws IOException {
-
Scanner
scan
=
new
Scanner(System.in);
-
try(
InputStream
inputStream
= socket.getInputStream();
-
OutputStream
outputStream
= socket.getOutputStream()) {
-
while(
true) {
-
//1.从控制台读取数据,构造成一个请求
-
System.out.println(
"-> ");
-
String
request
= scan.next();
-
-
//2.发送请求给服务器
-
PrintWriter
printWriter
=
new
PrintWriter(outputStream);
-
printWriter.println(request);
-
//这个 flush 不要忘记,否则可能导致请求没有真发出去
-
printWriter.flush();
-
-
//3.从服务器读取响应
-
Scanner
respScanner
=
new
Scanner(inputStream);
-
String
response
= respScanner.next();
-
-
//4.把响应显示到界面上
-
System.out.println(response);
-
}
-
}
-
}
-
-
public
static
void
main
(String[] args)
throws IOException {
-
TcpEchoClient
tcpEchoClient
=
new
TcpEchoClient(
"127.0.0.1",
9090);
-
tcpEchoClient.start();
-
}
-
}
7. 运行回显服务器和客户端
转载:https://blog.csdn.net/m0_58761900/article/details/127358914