(可按目录按需阅读,我一般会整理的比较细)

前置知识

java IO

Socket

什么是socket?socket字面意思其实就是一个插口或者套接字,包含了源ip地址、源端口、目的ip地址和源端口。
但是socket在那个位置呢 ,在TCP/IP网络的四层体系和OSI七层好像都找不到他的影子,如下图所示, Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。一般由操作系统或者JVM自己实现。java.net中的socket其实就是对底层的抽象调用。有一点需要注意,运行在同一主机上的其他应用程序可能也会通过底层套接字抽象来使用网络,因此会与java socket实例竞争资源,如端口。

工作流程
对于服务器来说,服务器先初始化socket,然后端口绑定(bind),再对端口监听(listen),调用accept阻塞,等待客户端连接请求。对于客户端来说,客户端初始化socket,然后申请连接(connection)。客户端申请连接,服务器接受申请并且回复申请许可(这里要涉及TCP三次握手连接),然后发送数据,最后关闭连接,这是一次交互过程。

ServerSocket

java.net.ServerSocket java 的实现
ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。

要创建一个服务器套接字,你需要使用 ServerSocket 类提供的四个构造方法中的一个。你 需要指定 IP 地址和服务器套接字将要进行监听的端口号。通常,IP 地址将会是 127.0.0.1,也 就是说,服务器套接字将会监听本地机器。服务器套接字正在监听的 IP 地址被称为是绑定地址。 服务器套接字的另一个重要的属性是 backlog,这是服务器套接字开始拒绝传入的请求之前,传 入的连接请求的最大队列长度。 其中一个 ServerSocket 类的构造方法如下所示:

public ServerSocket(int port, int backLog, InetAddress bindingAddress);

角色

服务器

服务器的socket程序有以下几个任务:

  • 创建ServerSocket。
  • 绑定并监听端口
  • 阻塞,等待客户端连接。
  • 与客户端连接成功后,进行数据交互

客户端

客户端的socket程序有以下几个任务:

  • 创建Socket。
  • 连接服务器。
  • 与服务器连接成功后,进行数据交互。

代码

使用Socket实现客户端和服务端的连接,其实网编程的本质就是进程之间的通信。服务端和客户端都会运行一个进程,然后进行数据交互,也就是数据的输入,输出。所以会涉及到IO

服务端代码

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) {final String QUIT = "quit";final int DEFAULT_PORT = 8000;ServerSocket serverSocket = null;BufferedReader reader = null;BufferedWriter writer = null;try {// 绑定监听端口serverSocket = new ServerSocket(DEFAULT_PORT);System.out.println("启动服务器,监听服务器本地端口" + DEFAULT_PORT);while (true) {// 等待客户端连接Socket socket = serverSocket.accept();System.out.println("客户端["+socket.getInetAddress()+":"+ socket.getPort() + "]已连接");reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));String msg = null;while ((msg = reader.readLine()) != null) {// 读取客户端发送的消息System.out.println("客户端["+socket.getInetAddress()+":"+ socket.getPort() + "]: " + msg);// 回复客户发送的消息writer.write("服务器已收到: " + msg + "\n");//防止消息遗留到本地缓冲区,保证马上发送出去writer.flush();// 查看客户端是否退出if (QUIT.equalsIgnoreCase(msg)) {System.out.println("客户端["+socket.getInetAddress()+":"+ socket.getPort() + "]已断开连接");break;}}}} catch (IOException e) {e.printStackTrace();} finally {try {serverSocket.close();reader.close();writer.close();System.out.println("关闭serverSocket");} catch (IOException e) {e.printStackTrace();}}}
}

客户端代码

import java.io.*;
import java.net.Socket;public class Client {public static void main(String[] args) {final String QUIT = "quit";final String DEFAULT_SERVER_HOST = "127.0.0.1";final int DEFAULT_SERVER_PORT = 8000;Socket socket = null;BufferedWriter writer = null;BufferedReader reader = null;BufferedReader consoleReader = null;try {// 创建socketsocket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);// 创建IO流reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));// 等待用户输入信息consoleReader = new BufferedReader(new InputStreamReader(System.in));while (true) {String input = consoleReader.readLine();// 发送消息给服务器writer.write(input + "\n");writer.flush();// 读取服务器返回的消息String msg = reader.readLine();System.out.println(msg);// 查看用户是否退出if (QUIT.equalsIgnoreCase(input)) {break;}}} catch (IOException e) {e.printStackTrace();} finally {try {writer.close(); //关闭之前还会flush一次socket.close();reader.close();consoleReader.close();System.out.println("关闭socket");} catch (IOException e) {e.printStackTrace();}}}
}

运行展示

客户端

服务端

CMD查看

开启Server端之后,在Windows cmd 终端里面输入命令,会发现8000端口处于LISTENING状态,先前没开启Server代码运行是没有的

netstat -ano|findstr "8000"

netstat 用于显示套接字内容 , -ano 是可选选项
a不仅显示正在通信的套接字,还显示包括尚未开始通信等状态的所有套接字
n 显示 IP 地址和端口号
o 显示套接字的程序 PID


第一列表示通信协议,这里是TCP
第二列表示,运行netstat命令的主机ip和port,这里也就是服务器的IP和port,0.0.0.0表示还没有绑定IP地址
第三列表示,通信对象的IP和port,0.0.0.0:0表示还没连接到对象,所以IP和port都不知道
第四列表示,LISTENING表示等待对方连接
最后一列,PID进程号
图中的每一行都相当于一个套接字,每一列也被称为一个元组,所以一个套接字就是五元组(协议、本地地址、外部地址、状态、PID),有的时候也被叫做四元组,四元组不包括协议。

开启客户端建立连接通信之后

可以看到开了两个进程,因为我们客户端和服务端都是在一个电脑上跑的,所以会出现这种情况。127.0.0.1是本机的环回地址。

端口号2381和我们程序中拿到的也是一样的。

扩展

服务端大致流程

在创建ServerSocket 实例的时候,他就已经监听了服务器本地的DEFAULT_PORT端口

serverSocket = new ServerSocket(DEFAULT_PORT);

进入构造函数(CTRL进入)

    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {// new 实例,里面还有一系列逻辑,里面有工厂可以new 实例setImpl();//删除了判断输入参数的代码 try {//绑定指定端口bind(new InetSocketAddress(bindAddr, port), backlog);}// 删除了出错和异常状态处理的代码}

bind 函数

public void bind(SocketAddress endpoint, int backlog) throws IOException {// 删除一系列if语句,用于判断业务的前提环境是否异常InetSocketAddress epoint = (InetSocketAddress) endpoint;try {//删除 SecurityManager 安全管理器的检查,检查该线程是否可以监听该端口//将此Serversocket绑定到指定的本地 IP 地址和端口号getImpl().bind(epoint.getAddress(), epoint.getPort());//实现对该端口的监听,backlog参数是socket上请求的最大挂起连接数getImpl().listen(backlog);bound = true; // 标志字段,表示绑定成功} //删除了出错和异常状态处理的代码}

所以,serversocket一经诞生就已经绑定监听了端口,不绑定监听端口说明没有构造完,这也是他天生的职责。

Socket socket = serverSocket.accept();

监听要与此套接字建立的连接并接受它。 该方法阻塞,直到建立连接

    public Socket accept() throws IOException {if (isClosed())throw new SocketException("Socket is closed");if (!isBound())throw new SocketException("Socket is not bound yet");// 老规矩,if检查Socket s = new Socket((SocketImpl) null);//安全管理器的checkAccept方法将使用s.getInetAddress().getHostAddress() s.getPort()和s.getPort()作为其参数调用,以确保允许操作。 这可能会导致 SecurityException。implAccept(s);return s;}

获取向客户端读、写的字符流。(socket的数据肯定是通过运输层协议通信而来的,而网络通信的数据一般为字节流数据,便于网络传输)
InputStreamReader 是字节流通向字符流的桥梁,它将字节流转换为字符流.
OutputStreamWriter是字符流通向字节流的桥梁,它将字符流转换为字节流.

BufferedReader
BufferedWriter
BufferedReader和BufferedWriter 获取到字符流后,可直接缓存,以增加缓冲的方式来提高输入和输出的效率

从read()方法理解,若使用InputStreamReader的read()方法,可以发现存在每2次就会调用一次解码器解码,但若是使用BufferedReader包装InputStreamReader后调用read()方法,可以发现只会调用一次解码器解码,其余时候都是直接从BufferedReader的缓冲区中取字符即可

从read(char cbuf[], int offset, int length)方法理解,若使用InputStreamReader的方法则只会读取leng个字符,但是使用BufferedReader类则会读取读取8192个字符,会尽量提取比当前操作所需的更多字节;

例如文件中有20个字符,我们先通过read(cbuf,0,5)要读取5个字符到数组cbuf中,然后再通过read()方法读取1个字符。那么使用InputStreamReader类的话,则会调用一次解码器解码然后存储5个字符到数组中,然后又调用read()方法调用一次解码器读取2个字符,然后返回1个字符;等于是调用了2次解码器,若使用BufferedReader类的话则是先调用一次解码器读取20个字符到字符缓冲区中,然后复制5个到数组中,在调用read()方法时,则直接从缓冲区中读取字符,等于是调用了一次解码器

因此可以看出BufferedReader类会尽量提取比当前操作所需的更多字节,以应该更多情况下的效率提升,因此在设计到文件字符输入流的时候,我们使用BufferedReader中包装InputStreamReader类即可

 reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

服务端一行一行的读取读取客户端发送的消息,客户端发送“quit”给服务器时,才表示此客户端要退出

String msg = null;
while ((msg = reader.readLine()) != null) {// 读取客户端发送的消息System.out.println("客户端["+socket.getInetAddress()+":"+ socket.getPort() + "]: " + msg);// 回复客户发送的消息writer.write("服务器已收到: " + msg + "\n");writer.flush();// 查看客户端是否退出if (QUIT.equalsIgnoreCase(msg)) {System.out.println("客户端["+socket.getInetAddress()+":"+ socket.getPort() + "]已断开连接");break;}
}

最后close()各种资源

finally {try {serverSocket.close();reader.close();writer.close();System.out.println("关闭serverSocket");} catch (IOException e) {e.printStackTrace();}}

客户端大致流程:

socket = new Socket(DEFAULT_SERVER_HOST , DEFAULT_PORT);

构造函数

//创建一个流套接字并将其连接到指定主机上的指定端口号。
public Socket(String host, int port) throws UnknownHostException, IOException{this(host != null ? new InetSocketAddress(host, port) :new InetSocketAddress(InetAddress.getByName(null), port),(SocketAddress) null, true);
}

他的跳转太多太细,我这里放一个执行到connect0()的调用栈,后缀一般带0的都是native方法。
connect0()方法会实现到服务器的连接
我们可以看到:连接业务的开始也是写到socket的构造函数里面的。我们new Socket(DEFAULT_SERVER_HOST , DEFAULT_PORT,里面会有专门的方法比如上面的new InetSocketAddress(host, port)来检查host,port的合法性然后生成/127.0.0.1:8000合法的格式(一个SocketAddress实例),总结起来就是一条业务链上,会横插入很多检查性的或者其他的业务代码,然后代码就会跳来跳去。

具体怎么连接传递数据已经被封装好了。

连接好了之后,同理获取向服务器读、写的字符流。

reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

写代码获取我们控制台的输入流

consoleReader = new BufferedReader(new InputStreamReader(System.in));

将从控制台获取的数据input,通过writer写入,来与服务器交互信息

while (true) {String input = consoleReader.readLine();// 发送消息给服务器writer.write(input + "\n");writer.flush();// 读取服务器返回的消息String msg = reader.readLine();System.out.println(msg);// 查看用户是否退出if (QUIT.equalsIgnoreCase(input)) {break;}
}

最后记得close() 各种资源

finally {try {writer.close(); //关闭之前还会flush一次socket.close();reader.close();consoleReader.close();System.out.println("关闭socket");} catch (IOException e) {e.printStackTrace();}
}

References:

  • https://kaven.blog.csdn.net/article/details/104149443
  • https://coding.imooc.com/class/381.html
  • https://www.cnblogs.com/liusxg/p/3917624.html
  • https://blog.csdn.net/jiaomingliang/article/details/45950591
  • https://www.cnblogs.com/winterfells/p/8745297.html
  • https://blog.csdn.net/ai_bao_zi/article/details/81134801
  • https://www.jianshu.com/p/42918db85f19
  • https://mp.weixin.qq.com/s/3Ma4nnkZNWiXacS7Ds2x4Q
  • https://www.pdai.tech/md/framework/tomcat/tomcat-x-design-web-container.html

java网络编程Socket实现客户端向服务端发送信息相关推荐

  1. 在Java中使用Socket模拟客户端和服务端(多线程)

    1:Socket与ServerSocket的交互 2.Socket和ServerSocket介绍 Socket 构造函数 Socket() Socket(InetAddress address, in ...

  2. [javaSE] 网络编程(浏览器客户端-自定义服务端)

    获取ServerSocket对象,new出来构造参数:int类型端口号 调用ServerSocket对象的accept()方法,得到Socket对象 获取PrintWriter对象,new出来,构造参 ...

  3. Golang使用协程通过Socket实现客户端向服务端发送数据,服务端接收数据

    一.服务端代码: package mainimport("fmt""net" )func process(conn net.Conn) {//延迟关闭目前连接d ...

  4. php winform通信,C# Winform 通过Socket实现客户端和服务端TCP通信

    操作界面如下: 1.声明Socket 第一个参数:寻址方式,第二个参数:传输数据的方式,第三个参数:通信协议 Socket socket = new Socket(AddressFamily.Inte ...

  5. java 网络编程(二) tcp传输实现客户端和服务端进行信息交流

    1.使用Tcp从一台电脑往另一台电脑上发送文本数据 客户端: import java.io.*; import java.net.*; /**** 客户端,* 通过查阅socket对象,发现在该对象建 ...

  6. Java中利用socket实现简单的服务端与客户端的通信(基础级)

    在上一篇文章中,简单的介绍了java中入门级的socket编程,简单的实现了客户端像服务器端发送数据,服务器端将数据接收并显示在控制台,没有涉及多线程.上一篇文章的链接:Java中利用socket实现 ...

  7. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  8. Java网络编程 Socket、ServerSocket 详解,方法介绍及完整代码示例

    Java网络编程 Socket.ServerSocket 详解,方法介绍及完整代码示例 概念 什么是网络编程? 网络编程是指编写运行在多个设备(计算机)的程序,这些设备通过网络连接起来.当这些通过网络 ...

  9. Java网络编程——Socket 编程

    Socket 编程 Socket编程是在TCP/IP上的网络编程,但是Socket在上述模型的什么位置呢.这个位置被一个天才的理论家或者是抽象的计算机大神提出并且安排出来 我们可以发现Socket就在 ...

最新文章

  1. 教育部:“博士分流淘汰制”真来了!不合格的导师,退出!
  2. C#保存文件为无BOM的utf8格式
  3. Nginx 的recv() failed 错误解决一例
  4. Python 读写文件和file对象(转)
  5. Java Spring里getBean方法的实现
  6. codeforces:CF1604 总结
  7. React开发(217):vs code出现‘npm’
  8. Groq新进展!谷歌TPU原班人马明年发布首款AI芯片
  9. rk3399_android7.1调试lsm9ds1三合一sensor记录
  10. Vbs 脚本编程简明教程之一
  11. 康佳LED55K55U电视板砖的拯救历程
  12. 夏令时到底是个什么东西?
  13. java prase xml error
  14. 尚硅谷谷粒商城第十二天 商品详情页及异步编排
  15. 养胃有道——送给胃不好的人
  16. 电子商务系统怎么开发,有哪些流程_OctShop
  17. ubuntu IOS文件下载
  18. gd32f470总结
  19. CPU速度慢了,怎么办?
  20. 画质、码率、帧数、分辨率、体积的基础编码知识

热门文章

  1. 文本框只允许输入数字
  2. 玛酷机器人与艾迪瑞特_玛酷机器人-逛超市
  3. 中运量71路线路图_[上海]配套中运量71路 公交1250路、1251路更名同步调整线路走向及设站...
  4. 花了148元!买了这份阿里内部面试神技——1658页《Java面试突击核心讲》
  5. POI-5.2.2 操作 word 【表格】
  6. django实战仿慕课网在线视频网站(完成了85%以上的功能已上传github:https://github.com/huwei86/mxonline):...
  7. UGUI图片拖拽_保留原图
  8. 用计算机表达爱你,表达我爱你的简短句子
  9. 怎样打开 CHM 文件
  10. CloudStack+XenServer详细部署方案 交换机配置和服务器连线