什么是Socket

Socket的概念很简单,它是网络上运行的两个程序间双向通讯的一端,既可以接收请求,也可以发送请求,利用它可以较为方便地编写网络上数据的传递

所以简而言之,Socket就是进程通信的端点,Socket之间的连接过程可以分为几步:

1、服务器监听

服务器端Socket并不定位具体的客户端Socket,而是处于等待连接的状态,实时监控网络状态

2、客户端请求

客户端Socket发出连接请求,要连接的目标是服务端Socket。为此,客户端Socket必须首先描述它要连接的服务端Socket,指出服务端Socket的地址和端口号,然后就向服务端Socket提出连接请求

3、连接确认

当服务端Socket监听到或者说是接收到客户端Socket的连接请求,它就响应客户端Socket的请求,建立一个新的线程,把服务端Socket的描述发给客户端,一旦客户端确认了此描述,连接就好了。而服务端Socket继续处于监听状态,继续接收其他客户端套接字的连接请求

TCP/IP、HTTP、Socket的区别

这三个概念是比较容易混淆的概念,这里尽量解释一下三者之间的区别。

随着计算机网络体系结构的发展,OSI七层网络模型诞生了,这个模型把开放系统的通信功能划分为七个层次,一次完整的通信如下图:

每一层都是相互独立的,它利用其下一层提供的服务并为其上一层提供服务,而与其它层的具体实现无关,所谓"服务"就是下一层向上一层提供的通信功能和层之间的会话约定,一般用通信原语实现。上图中,从下至上分别给层编号为1~7,其中1~4层为下层协议,5~7层为上层协议,接着回到我们的概念:

1、TCP/IP讲的其实是两个东西:TCP和IP。IP是一种网络层的协议,用于路由选择、网络互连

2、TCP是一种传输层协议,用于建立、维护和拆除传送连接,在系统之间提供可靠的透明的数据传送

3、HTTP是一种应用层协议,提供OSI用户服务,例如事物处理程序、文件传送协议和网络管理等,其目的最终是为了实现应用进程之间的信息交换

至于Socket,它只是TCP/IP网络的API而已,Socket接口定义了许多函数,用以开发TCP/IP网络上的应用程序,组织数据,以符合指定的协议。

Socket的两种模式

Socket有两种主要的操作方式:面向连接和无连接的。面向连接的Socket操作就像一部电话,必须建立一个连接和一人呼叫,所有事情在达到时的顺序与它们出发时的顺序一样,无连接的Socket操作就像是一个邮件投递,没有什么保证,多个邮件可能在达到时的顺序与出发时的顺序不一样。

到底使用哪种模式是由应用程序的需要决定的。如果可靠性更重要的话,用面向连接的操作会好一些,比如文件服务器需要数据的正确性和有序性,如果一些数据丢失了,系统的有效性将会失去;比如一些服务器间歇性地发送一些数据块,如果数据丢失了的话,服务器并不想要再重新发送一次,因为当数据到达的时候,它可能已经过时了。确保数据的有序性和正确性需要额外的操作的内存消耗,额外的消耗将会降低系统的回应速率。

无连接的操作使用数据报协议。一个数据报是一个独立的单元,它包含了所有这次投递的信息,就像一个信封,它有目的地址和要发送的内容,这个模式下的Socket并不需要连接一个目的Socket,它只是简单地透出数据报,无连接的操作是快速、高效的,但是数据安全性不佳。

面向连接的操作使用TCP协议。一个这个模式下的Socket必须在发送数据之前与目的地的Socket取得一个连接,一旦连接建立了,Socket就可以使用一个流接口:打开-->读-->写-->关闭,所有发送的信息都会在另一端以同样的顺序被接收。面向连接的操作比无连接的操作效率更低,但是数据的安全性更高。

利用Java开发Socket

在Java中面向连接的类有两种形式,它们分别是客户端和服务器端,先看一下服务器端:

public class HelloServer
{
public static void main(String[] args) throws IOException
{
ServerSocket serverSocket = null; try
{
// 实例化一个服务器端的Socket连接
serverSocket = new ServerSocket(9999);
}
catch (IOException e)
{
System.err.print("Could not listen on port:9999");
System.exit(1);
} Socket clientSocket = null;
try
{
// 用于接收来自客户端的连接
clientSocket = serverSocket.accept();
}
catch (IOException e)
{
System.err.println("Accept failed");
System.exit(1);
} // 客户端有数据了就向屏幕打印Hello World
System.out.print("Hello World");
clientSocket.close();
serverSocket.close();
}
}

此代码的作用就是构造出服务端Socket,并等待来自客户端的消息。当然,此时运行代码是没有任何反应的,因为服务端在等待客户端的连接。下面看一下客户端代码如何写:

 public class HelloClient
{
public static void main(String[] args) throws IOException
{
Socket socket = null;
BufferedReader br = null; // 下面这段程序,用于将输入输出流和Socket相关联
try
{
socket = new Socket("localhost", 9999);
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
catch (UnknownHostException e)
{
System.err.println("Don't know about host:localhost");
System.exit(1);
}
catch (IOException e)
{
System.err.println("Could not get I/O for the connection");
System.exit(1);
} System.out.print(br.readLine());
br.close();
socket.close();
}
}

此时只需要先运行HelloServer,再运行HelloClient,保证服务器先监听,客户端后发送,就可以在控制台上看到"Hello World"了。

改进版本的Socket

上面的Socket演示的效果是,服务器端Socket收到了来自客户端Socket的数据,但是并没有真正地体现服务器端Socket和客户端Socket的交互,下面演示一下利用Socket进行服务器端和客户端的交互,首先是服务器端的:

 public class EchoServer
{
public static void main(String[] args) throws IOException
{
ServerSocket ss = null;
PrintWriter pw = null;
BufferedReader br = null; try
{
// 实例化监听端口
ss = new ServerSocket(1111);
}
catch (IOException e)
{
System.err.println("Could not listen on port:1111");
System.exit(1);
}
Socket incoming = null;
while (true)
{
incoming = ss.accept();
pw = new PrintWriter(incoming.getOutputStream(), true);
// 先将字节流通过InputStreamReader转换为字符流,之后将字符流放入缓冲之中
br = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
// 提示信息
pw.println("Hello!...");
pw.println("Enter BYE to exit");
pw.flush();
// 没有异常则不断循环
while (true)
{
// 只有当用户输入时才返回数据
String str = br.readLine();
// 当用户连接断掉时会返回空值null
if (str == null)
{
// 退出循环
break;
}
else
{
// 对用户输入字符串加前缀Echo并将此信息打印到客户端
pw.println("Echo:" + str);
pw.flush();
// 退出命令,equalsIgnoreCase()是不区分大小写的
if ("BYE".equalsIgnoreCase(str.trim()))
{
break;
}
}
}
// 该close的资源都close掉
pw.close();
br.close();
incoming.close();
ss.close();
}
}
}

接着是客户端的:

 public class EchoClient
{
public static void main(String[] args) throws IOException
{
Socket socket = null;
PrintWriter pw = null;
BufferedReader br = null; try
{
socket = new Socket("localhost", 1111);
pw = new PrintWriter(socket.getOutputStream(), true);
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
catch (UnknownHostException e)
{
System.err.println("Don't know abount host:localhost");
System.exit(1);
}
System.out.println(br.readLine());
System.out.println(br.readLine());
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String userInput;
// 将客户端Socket输入流(即服务器端Socket的输出流)输出到标准输出上
while ((userInput = stdIn.readLine()) != null)
{
pw.println(userInput);
System.out.println(br.readLine());
}
// 同样的,将该关闭的资源给关闭掉
pw.close();
br.close();
socket.close();
}
}

看一下运行结果:

这正是我们程序要达到的效果,客户端不管输入什么,服务器端都给输入拼接上"Echo:"返还给客户端并打印在屏幕上。

服务端多监听

程序写到上面,已经基本成型了,不过还有一个问题:现实情况中,一个服务器端的Socket不可能只对应一个客户端的Socket,必然一个服务器端的Socket可以接收来自多个客户端的Socket的请求。

解决上述问题的办法就是多线程。大致代码是这样的:

public class HandleThread extends Thread
{
private Socket socket; public HandleThread(Socket socket)
{
this.socket = socket;
} public void run()
{
// Socket处理代码
}
}
public static void main(String[] args) throws IOException
{
ServerSocket serverSocket = null; try
{
// 实例化一个服务器端的Socket连接
serverSocket = new ServerSocket(9999);
}
catch (IOException e)
{
System.err.print("Could not listen on port:9999");
System.exit(1);
} Socket clientSocket = null;
try
{
while (true)
{
// 用于接收来自客户端的连接
clientSocket = serverSocket.accept();
new HandleThread(clientSocket).start();
}
}
catch (IOException e)
{
System.err.println("Accept failed");
System.exit(1);
}
}

即,服务器端启动一个永远运行的线程,监听来自客户端的Socket,一旦客户端有Socket到来,即开启一个新的线程将Socket交给线程处理。

由服务端多监听程序看IO模型

上面的代码,用一张图来表示一下这种IO模型:

即由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型,也就是Blocking IO模型即BIO。

该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增大后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程是Java虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将极具下降,随着并发访问量的继续增大,系统将会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。

在高性能服务器应用领域,往往要面向成千上万个客户端的并发连接,这种模型显然无法满足高性能、高并发接入的场景。

当然具体问题具体分析,BIO性能虽然差,但是编程简单,如果客户端并发连接数不多,周边对接的网元不多,服务器的负载也不重,那么完全可以使用BIO进行作为服务器的IO模型。

Java Socket的更多相关文章

  1. JAVA通信系列一:Java Socket技术总结

    本文是学习java Socket整理的资料,供参考. 1       Socket通信原理 1.1     ISO七层模型 1.2     TCP/IP五层模型 应用层相当于OSI中的会话层,表示层, ...

  2. JAVA Socket 编程学习笔记(二)

    在上一篇中,使用了 java Socket+Tcp/IP  协议来实现应用程序或客户端--服务器间的实时双向通信,本篇中,将使用 UDP 协议来实现 Socket 的通信. 1. 关于UDP UDP协 ...

  3. JAVA Socket 编程学习笔记(一)

    1. Socket 通信简介及模型 Java Socket 可实现客户端--服务器间的双向实时通信.java.net包中定义的两个类socket和ServerSocket,分别用来实现双向连接的cli ...

  4. Java Socket Server的演进 (一)

    最近在看一些网络服务器的设计, 本文就从起源的角度介绍一下现代网络服务器处理并发连接的思路, 例子就用java提供的API. 1.单线程同步阻塞式服务器及操作系统API 此种是最简单的socket服务 ...

  5. JAVA Socket超时浅析

    JAVA Socket超时浅析 套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的"终端".针对一个特定的连接,每台机器上都有一个"套接字&q ...

  6. Java Socket编程题库

    一.    填空题 ___ IP地址____用来标志网络中的一个通信实体的地址.通信实体可以是计算机,路由器等. 统一资源定位符URL是指向互联网"资源"的指针,由4部分组成:协议 ...

  7. Java Socket编程(转)

    Java Socket编程 对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首 ...

  8. 交通银行 Java Socket 服务启动 管理 WINDOWS 版

    按照交通银行提供的无界面启动方法试验了很多次,都没有成功,所以自己动手用C# 知识写了一个. 小工具可以判断 交通银行 JAVA SOCKET 服务是否启动,并可以启动/关闭服务 主要代码如下: 判断 ...

  9. Java Socket 网络编程心跳设计概念

    Java Socket 网络编程心跳设计概念   1.一般是用来判断对方(设备,进程或其它网元)是否正常动行,一 般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉.用于 ...

  10. JAVA Socket 实现HTTP与HTTPS客户端发送POST与GET方式请求

    JAVA Socket 实现HTTP与HTTPS客户端发送POST与GET方式请求 哇,一看标题怎么这么长啊,其实意思很简单,哥讨厌用HTTP Client做POST与GET提交 觉得那个毕竟是别人写 ...

随机推荐

  1. 重置VS设置

    用VS开发项目的时候, 偶发智能提示消失.关键字.类名不变色的情况. 如果你也遇到过,那么这样做: 新建txt,打开并输入以下内容: start "" "C:\Progr ...

  2. UWP Composition API - PullToRefresh

    背景: 之前用ScrollViewer 来做过 PullToRefresh的控件,在项目一些特殊的条件下总有一些问题,比如ScrollViewer不会及时到达指定位置.于是便有了使用Compositi ...

  3. Linux学习笔记(11)-kill函数

    明天开始学习kill函数的用法. ---------------------------------------------- kill函数可以用来向指定的进程发送一个指定的信号,在我的理解的来看,就 ...

  4. Maven+Spring Profile实现生产环境和开发环境的切换

    第一步 Maven Profile配置 <profiles> <profile> <id>postgres</id> <activation> ...

  5. Django框架学习

    两个月前学习的Django框架,写了个简易婚恋调查网站,代码就懒得全贴了,有两张图记录下

  6. Junit测试框架 Tips

    关于Junit测试框架使用的几点总结: 1.Junit中的测试注解: @Test →每个测试方法前都需要添加该注解,这样才能使你的测试方法交给Junit去执行. @Before →在每个测试方法执行前 ...

  7. PC端一些非经典兼容性问题小札

    IE10默认在input框中输入内容后会显示一个‘X’按钮,方便删除输入的所有内容. 在password输入框显示一个眼睛的按钮,去掉叉叉和眼睛的方法如下: ::-ms-clear { display ...

  8. 初探React-Native

    props 大多数组件在创建时就可以使用各种参数来进行定制.用于定制的这些参数就称为props(属性). 以常见的基础组件Image为例,在创建一个图片时,可以传入一个名为source的prop来指定 ...

  9. [field:picname/]和[field:litpic/]区别

    显示出二级栏目及以下的所有图片(包含三级栏目的) <ul class="incps"> {dede:arclist row=9 col="3" ty ...

  10. Where product development should start

    We all need to know our customers in order to create products they’ll actually buy. This is why the  ...