这里写目录标题
- 一、API介绍
- ServerSocket
- Socket
- 二、创建简单的回显服务器
- 服务器端
- 客户端
一、API介绍
ServerSocket
- 构造方法
方法签名 | 方法说明 |
---|---|
ServerSocket(int port) | 创建⼀个服务端流套接字Socket,并绑定到指定端⼝ |
关于此构造方法的注意事项:
Server的构造方法不止这一个,但是表格中的是最常用的。 ServerSocket(int
port) 不用指定服务器的IP地址,它会自动监听所有网络接口,在运行之前它的IP地址用通配符表示,服务器实际运行的IP地址取决于服务器的运行环境。 如果是在本地电脑,那么IP地址就是“127.0.0.1”,如果使用的是公网,那么IP地址就是公网IP,因此程序用就不用自己手动设置服务器的IP地址了
- 类方法
方法签名 | 方法说明 |
---|---|
Socket accept | 开始监听指定端口(ServerSocket创建绑定的端口),有客户端连接后,返回一个服务端的Socket对象,并基于该Socket建立与客户但的连接,否则进入阻塞等待 |
void close() | 关闭套接字 |
注意:
只要使用套接字就会消耗文件资源,也就是会占用文件资源描述符。如果使用完套接字,不手动close()关闭,有时出现大量文件资源申请,文件资源描述符被使用完就可能出现文件资源泄露。为了保证代码的健壮性,一定要记得判断是否需要手动关闭文件资源。
Socket
- 构造方法
方法签名 | 方法说明 |
---|---|
Socket(String host, int port) | 创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接 |
- 类方法
方法签名 | 方法说明 |
---|---|
InetAddress getInetAddress() | 返回套接字所连接的地址 |
in getPort() | 返回套接字所连接的端口号 |
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
二、创建简单的回显服务器
回显服务器
(Echo Server)是一种简单的服务器应用程序,它的功能是接收客户端发送的数据,并将这些数据原样返回给客户端。这种服务器通常用于测试和调试网络应用程序。
服务器端
public class Server {//用于与客户端建立连接private ServerSocket serverSocket = null;public Server(int port) throws IOException {//这里只用设置端口号即可,不用设置IP地址,IP地址根据运行环境而定//因为是在本地运行,所以运行时IP地址时“127.0.0.1”serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");while (true) {//与UDP协议不同,TCP协议需要和客户端建立连接Socket clientSocket = serverSocket.accept();System.out.printf("服务器和客户端建立连接 [%s:%d]\n",clientSocket.getInetAddress(),clientSocket.getPort());//使用线程池,确保一个服务器可以同时处理多个客户端发来的请求Thread thread = new Thread(() -> {try {//正式进行数据传输processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}});//记得启动线程!thread.start();}}private void processConnection(Socket clientSocket) throws IOException {//TC面向字节流,进行传输,所以使用OutputStream/InputStreamtry(OutputStream outputStream=clientSocket.getOutputStream();InputStream inputStream=clientSocket.getInputStream()) {//用于获取客户端的请求Scanner scanner=new Scanner(inputStream);//用于发送响应给客户端PrintWriter printWriter=new PrintWriter(outputStream);//循环处理请求与响应while(true){//1、获取客户端请求if(!scanner.hasNext()){//如果没有得到客户端的请求,说明客户端下线了break;}//得到的请求放到request中String request=scanner.next();//2、根据请求,生成响应String response=process(request);//3、把响应发送给客户端printWriter.println(response);printWriter.flush();//记得刷新缓冲区!!!详细解释见代码块下方注意事项//4、打印工作日志System.out.printf("[%s:%d] 请求=%s 响应=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}} catch (IOException e) {e.printStackTrace();}finally {//打印客户端下线信息System.out.printf("客户端下线 [%s:%d]\n",clientSocket.getInetAddress(),clientSocket.getPort());//只要是套接字,就会消耗资源占位符,如果用完没有及时关闭,可能出现文件资源泄露。if(clientSocket!=null&&!clientSocket.isClosed()){clientSocket.close();}}}//因为是回显服务器,所以这里直接返回相同的内容,在实际开发中这一步可能是比较复杂的private String process(String request){return request;}public static void main(String[] args) throws IOException {//自己指定一个端口号最好是大于1023的(当然也要小于65535)Server server=new Server(9090);server.start();}
}
注意事项:
PrintWriter的发送数据设置了缓冲区。 设置一个缓冲区的原因是避免频繁进行数据IO,因为这样消耗大量资源。通过缓冲区,数据堆积到一定大小在进行IO那么消耗成本就会小很多。但是在我们这个回显服务器中,需要立刻看到响应,所以需要用到PrintWriter中的flush()方法,调用这个方法可以刷新缓冲区,把所有的数据都进行IO。
另外, 除了用PrintWriter进行数据IO ,还可以使用outputStream
的write()
方法,但是需要把数据转化成字节数组:
客户端
public class Client {//用于接收服务器的响应,或者发送请求给服务器private Socket socket=null;//两个参数分别是,需要连接的服务器的IP地址以及端口号,端口号刚才设置了是9090,由于是在本地运行所以ip默认是“127.0.0.1”public Client(String serverIP,int port) throws IOException {//和指定的服务器建立连接socket=new Socket(serverIP,port);}public void start() throws IOException {System.out.println("客户端启动!");//与UDP不同,TCP面向字节流进行传输try(OutputStream outputStream=socket.getOutputStream();InputStream inputStream=socket.getInputStream()){//用户通过控制台输入,提供请求Scanner userIN=new Scanner(System.in);//用于发送请求PrintWriter printWriter=new PrintWriter(outputStream);//用于接收响应Scanner recieve=new Scanner(inputStream);//循环运行while(true){//1、获取用户请求System.out.print("->");String request=userIN.next();//2、把请求发送给客户端printWriter.println(request);printWriter.flush();//注意记得刷新缓冲区!//3、接收服务器发来的响应if(!recieve.hasNext()){//没有获取到服务器的响应,可能是网络出现问题,或者服务器挂了break;}//正确获取到响应String response=recieve.next();//4、把响应呈现给用户(打印)System.out.printf("响应:%s\n",response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {Client client=new Client("127.0.0.1",9090);client.start();}
}
最后,如果有任何关于代码或概念上的问题,欢迎私信或者评论交流。关于TCP协议的相关机制,可以参阅: 网络编程专栏 大家的支持是我前行的巨大动力!