当前位置: 首页 > news >正文

【Javaee】网络编程-TCP Socket

前言

前文中我们介绍了UDP Socket相关的构造方法和方法,并实现了UDP的回显服务器和客户端。

本篇将介绍TCP Socket,并使用TCP Socket api实现服务器和客户端的通信


一.TCP Socket的常见方法

1.ServerSocket

ServerSocket是创建TCP服务端Socket的API

1)ServerSocket构造方法

方法签名方法说明
ServerSocket(int port)创建⼀个服务端流套接字Socket,并绑定到指定端⼝

2)ServerSocket方法

方法签名方法说明
Socket accept()开始监听指定端⼝(创建时绑定的端⼝),有客⼾端连接后,返回⼀个服务端Socket对象,并基于该 Socket建⽴与客⼾端的连接,否则阻塞等待
void close()

关闭此套接字

TCP建立连接的流程是操作系统内核完成的,代码感知不到,accept()操作是内核已经完成了连接建立的操作,accept()是确定对该连接进行处理(确认操作)。

如果没有客户端请求,则会阻塞。

accept会返回一个Socket对象,服务器每调用一次accept都会产生一个新的Socket对象,来和客户端进行“一对一服务”。

2.Socket

Socket 是客⼾端Socket,或是服务端中接收到客⼾端建⽴连接(accept⽅法)的请求后,返回的服务端Socket。

不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对端信息,及⽤来与对⽅收发数据的。

1)Socket构造方法

方法签名方法说明
Socket(String host,int Port)创建⼀个客⼾端流套接字Socket,并与对应IP的主机上,对应端⼝的进程建⽴连接

2)Socket方法

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输⼊流
OutputStream getOutputStream()返回此套接字的输出流

二.TCP服务器端实现

1.代码实现

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.security.Provider;
import java.util.Scanner;public class TcpServer {private ServerSocket serverSocket=null;public TcpServer(int port) throws IOException {serverSocket=new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动");while(true){Socket client= serverSocket.accept();processConnection(client);}}private void processConnection(Socket client) {System.out.printf("[%s :%d]客户端上线",client.getInetAddress(),client.getPort());try(InputStream inputStream=client.getInputStream();//TCP是全双工通信,既能读,也能写OutputStream outputStream=client.getOutputStream();PrintWriter printWriter=new PrintWriter(outputStream)){while(true){//1.读取请求并解析(为方便读,可使用Scanner)Scanner scanner=new Scanner(inputStream);if(!scanner.hasNext()){//如果Scanner读不出下一个数据了,说明客户端关闭了连接,导致服务器读到了“末尾”break;}//2.根据请求计算响应String request=scanner.next();String response=process(request);//3.把响应写回客户端printWriter.println(response);//4.打印日志System.out.printf("[%s :%d], rep=%s,reps=%s\n",client.getInetAddress(),client.getPort(),request,response);}}catch (IOException e){e.printStackTrace();}System.out.printf("[%s :%d]服务器下线",client.getInetAddress(),client.getPort());}private String process(String response){return response;}public static void main(String[] args) throws IOException {TcpServer server=new TcpServer(9090);server.start();}
}

2.代码解析

1.建立ServerSocket类,需填入参数来绑定程序的端口号,new ServerSocket(port)后于客户端建立起连接。

2.创建start()方法启动服务器,使用accept()确认对连接客户端进行处理,将处理过程传入processConnection()函数中

3.因TCP服务器是面向字节流,因此需要使用输入流与输出流进行数据的发送与接收。(在try中定义是因为try结束后会自动释放文件资源)

4.进行数据的读取,读取完后对数据进行处理(process()中处理,因本代码为回显服务器,只是返回客户端请求,无更多逻辑),处理完后生成响应,并写回客户端,最后打印日志

注:此处服务器代码存在三个问题,需结合客户端代码进行解释与修改

三.TCP客户端实现

1.代码实现

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpCilent {private Socket socket=null;public TcpCilent(String ServerIP,int Port) throws IOException {socket=new Socket(ServerIP,Port);}public void strat(){System.out.println("客户端启动");try(InputStream inputStream= socket.getInputStream();OutputStream outputStream=socket.getOutputStream();Scanner scanner=new Scanner(inputStream);PrintWriter printWriter=new PrintWriter(outputStream);Scanner scannerIn=new Scanner(System.in)){while(true){//1.从控制台读取信息String request=scannerIn.next();//2.将数据发送给服务器printWriter.println(request);//3.从服务器获取响应if(!scannerIn.hasNext()){break;}String response=scanner.next();//4.打印日志System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpCilent cilent=new TcpCilent("127.0.0.1",9090);cilent.strat();}
}

2.代码解析

1.客户端创建Socket类,需填入服务器的ip和端口号,在new时系统内核会自与对应的服务器连接上

2.与服务器同理,因TCP面向字节流,需要通过输入流与输出流来实现数据的发送与接收。

3.从控制台中读取数据(我们输入的),然后将其写入到服务器,在获取到服务器的响应,最后打印日志。

四.问题与解决

问题一:客户端发送数据之后,并没有任何响应(缓冲区问题)

运行上述代码,服务器成功打印出客户端上线和下线

但是客户端发送数据,服务器并无响应

原因是像PrintWriter这样的类,以及很多IO流中的类,都是自带“缓冲区”的。

引入缓冲区后,进行写入操作时不会立即触发IO,而是会将数据先放入缓冲区,等到缓冲区的数据积攒一波后,在统一发送(因为频繁的IO操作开销大,这是一个优化)

解决上诉问题,只要通过flush刷新缓冲区就可解决。

问题二:服务器代码未对client进行close()操作

这里的client是“连接级别”的数据,随着客户端断开连接,这个client也就不再使用了。

即使是同一个客户端再次连接,也是一个新的client,和旧的不是同一个

因此,这里的client就应该主动关闭掉。

可以在finally关闭

问题三:尝试多个客户端来同时连接服务器

作为一个服务器,就是要同时给多个客户端进行服务的

当前代码,只能启动一个客户端,如何启动多个客户端?

要修改一些配置(如图)

此时,我们发现,我们可以启动多个客户端

但是,利用新创建的客户端向服务器发送数据,却没有响应。

若把第一个客户端关掉,服务器就会立刻针对客户端2的请求进行响应。

此时,我们的服务器只能同时给一个客户端进行服务,这是不科学的。

原因是当第一个客户端连接上服务器,服务器代码就会进入processConnection内部的while循环,此时第二个客户端无法执行到accept,使用操作积压在内核缓冲区中。

解决方法是将双重while循环改为单层while循环,我们可以利用多线程

这样,问题就得到了解决

两个客户端是不同的端口

可以共同使用服务器。


完整代码

1.客户端代码

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpCilent {private Socket socket=null;public TcpCilent(String ServerIP,int Port) throws IOException {socket=new Socket(ServerIP,Port);}public void strat(){System.out.println("客户端启动");try(InputStream inputStream= socket.getInputStream();OutputStream outputStream=socket.getOutputStream();Scanner scanner=new Scanner(inputStream);PrintWriter printWriter=new PrintWriter(outputStream);Scanner scannerIn=new Scanner(System.in)){while(true){//1.从控制台读取信息String request=scannerIn.next();//2.将数据发送给服务器printWriter.println(request);printWriter.flush();//3.从服务器获取响应if(!scannerIn.hasNext()){break;}String response=scanner.next();//4.打印日志System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpCilent cilent=new TcpCilent("127.0.0.1",9090);cilent.strat();}
}

2.服务器端代码

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.security.Provider;
import java.util.Scanner;public class TcpServer {private ServerSocket serverSocket=null;public TcpServer(int port) throws IOException {serverSocket=new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动");while(true){Socket client= serverSocket.accept();Thread t=new Thread(()->{try {processConnection(client);} catch (IOException e) {e.printStackTrace();}});t.start();}}private void processConnection(Socket client) throws IOException {System.out.printf("[%s :%d]客户端上线\n",client.getInetAddress(),client.getPort());try(InputStream inputStream=client.getInputStream();//TCP是全双工通信,既能读,也能写OutputStream outputStream=client.getOutputStream();PrintWriter printWriter=new PrintWriter(outputStream)){while(true){//1.读取请求并解析(为方便读,可使用Scanner)Scanner scanner=new Scanner(inputStream);if(!scanner.hasNext()){//如果Scanner读不出下一个数据了,说明客户端关闭了连接,导致服务器读到了“末尾”break;}//2.根据请求计算响应String request=scanner.next();String response=process(request);//3.把响应写回客户端printWriter.println(response);printWriter.flush();//4.打印日志System.out.printf("[%s :%d], rep=%s,reps=%s\n",client.getInetAddress(),client.getPort(),request,response);}}catch (IOException e){e.printStackTrace();}finally {System.out.printf("[%s :%d]服务器下线\n",client.getInetAddress(),client.getPort());client.close();}}private String process(String response){return response;}public static void main(String[] args) throws IOException {TcpServer server=new TcpServer(9090);server.start();}
}

以上便是全部内容,如有不对,欢迎指正


http://www.mrgr.cn/news/55041.html

相关文章:

  • <项目代码>YOLOv8路面病害识别<目标检测>
  • 在win系统上做生信数据分析如何快速检查和填写正确的文件路径
  • 八股面试2(自用)
  • JS事件和DOM
  • LCWLAN设备的实际使用案例
  • 关于鸿蒙学习之遇到的问题——ERROR: Invalid dependency entry
  • Linux常用命令详细解析(含完整命令演示过程)
  • windows C++ 有效利用异步代理库(二)
  • 上海市货运资格证二寸照片要求及手机拍照方法
  • C++编程语言:抽象机制:运算符重载(Bjarne Stroustrup)
  • PostgreSQL模板数据库template0和template1的异同点
  • 033 商品搜索
  • 音视频入门基础:FLV专题(17)——FFmpeg源码中,提取Video Tag的VIDEODATA的实现
  • Linux:基础IO
  • 软件测试技巧-如何定位前后端bug?
  • 营销新境界:解码品牌增长策略
  • [OpenCV] 数字图像处理 C++ 学习——17模板匹配详细讲解+附完整代码
  • 3.订阅者Subscriber的编程实现以及话题消息定义与使用后续课程
  • pgAdmin不显示template1数据库,该如何设置才可以显示?
  • ACM与蓝桥杯竞赛指南 基本输入输出格式二
  • 波浪理论(Elliott Wave Theory)
  • autosar-port/interface学习总结
  • Docker compose 安装Jenkins
  • c++迷宫游戏
  • 揭秘CSS浮动盒:掌握高度塌陷修复、文字环绕特效示艺的秘籍!!(重点秘籍!!)
  • 高清无水印推文视频素材下载网站推荐