Java Socket

什么是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教学资源。帮助大家在成为Java架构师的道路上披荆斩棘。需要资料的欢迎加入学习交流群:9285,05736

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/503142.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

40个Java 多线程问题总结

1、多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然"&am…

学好Java的八个条件

世界上没有什么捷径可以成为大师,但老师告诉我们学习Java的一些基本原则是可以遵循的。接下来,前锋广州老师分享了学习Java编程的8个条件。你有多少? 1。坚实基础 数据结构、离散数学和编译原理是所有计算机科学的基础。如果我们不掌握它们&…

Java面向对象基础接口和抽象的理解

第一,抽象类: 我们都知道,在面向对象的领域中,一切都是一个对象,所有的对象都是用类来描述的,但不是所有的类都是用对象来描述的。如果一个类没有足够的信息来描述一个特定的对象,并且需要其他…

java中容易被忽视的基本概念

概念1: try-catch-finally块中,finally块在以下几种情况将不会执行。 (1)finally块中发生了异常。 (2)程序所在线程死亡。 (3)在前面的代码中用了System.exit(&#…

JAVA四种遍历Map的方法

导入java.util.hashmap&#xff1b; 导入java.util.iterator&#xff1b; 导入java.util.map&#xff1b; 导入java.util.set&#xff1b; 公共类映射{ 公共静态void main&#xff08;string[]args&#xff09;{ mapmapnew hashmap<>&#xff08;&#xff09;&…

spring中基于Java容器配置注解的区别及使用场景

转载自百家号作者&#xff1a;有趣的代码 Component、Service、Controller、Repository&#xff1b;Configuration、Bean区别&#xff0c;使用场景 从spring3.0开始&#xff0c;基于javaconfig的项目&#xff0c;支持使用java来定义bean&#xff0c;而不是传统的xml文件。Comp…

Java的文件流操作

文件系统 FileSystem类的对象表示Java程序中的文件系统。 FileSystem对象用于执行两个任务&#xff1a; Java程序和文件系统之间的接口。 一个工厂用于创建许多类型的文件系统相关对象和服务。 FileSystem对象与平台相关。 创建文件系统 要获取默认的FileSystem对象&…

学习Java编程,英语对我们来说有多重要?

Java软件开发需要学英语吗&#xff1f;学软件开发大家都知道&#xff0c;动手写代码远远比那些个只看理论知识要强得多。很多人因为不懂软件开发流程&#xff0c;就会觉得软件开发肯定很难&#xff0c;实际上不然&#xff0c;软件开发并没有想象中的那么难&#xff0c;也完全没…

Java基础学习需要掌握哪些内容?

对于零基础或者打算开始学习Java编程的同学&#xff0c;首先要去学习Java基础课程的内容&#xff0c;那么Java基础课程有哪些呢&#xff1f;无非就是需要知道Java概述、Java语言基础、面向对象、异常处理、常用类、数组和集合、IO流、线程、反射等。 Java概述&#xff1a;你要知…

Java中高效判断数组中是否包含某个元素

如何检查一个数组(无序)是否包含一个特定的值&#xff1f;这是一个在Java中经常用到的并且非常有用的操作。同时&#xff0c;这个问题在Stack Overflow中也是一个非常热门的问题。在投票比较高的几个答案中给出了几种不同的方法&#xff0c;但是他们的时间复杂度也是各不相同的…

最常问的Java面试题,有Get吗?

1. import java和javax有什么区别 刚开始的时候JavaAPI所必需的包是java开头的包&#xff0c;javax当时只是扩展API包来说使用。然而随着时间的推移&#xff0c;javax逐渐的扩展成为Java API的组成部分。但是&#xff0c;将扩展从javax包移动到java包将是太麻烦了&#xff0c;…

Java的类型擦除

一、各种语言中的编译器是如何处理泛型的 通常情况下&#xff0c;一个编译器处理泛型有两种方式&#xff1a; 1.Code specialization。在实例化一个泛型类或泛型方法时都产生一份新的目标代码&#xff08;字节码or二进制代码&#xff09;。例如&#xff0c;针对一个泛型list&…

如何高效学习java呢?

1.基础阶段的学习 在这个学习阶段你应该找一个好的基础学习视频&#xff0c;对着视频认真进行学习&#xff0c;每天自己要逼着自己学习&#xff0c;大学很容易让人颓废。边看还要变敲代码&#xff0c;不要用ide用记事本有利于后期的代码编写习惯&#xff0c;还要做好笔记哦。 …

Java中数组的初始化

一、什么是初始化 在Java程序开发中&#xff0c;使用数组之前都会对其进行初始化&#xff0c;这是因为数组是引用类型&#xff0c;声明数组只是声明一个引用类型的变量&#xff0c;并不是数组对象本身&#xff0c;只要让数组变量指向有效的数组对象&#xff0c;程序中就可使用…

Java程序员如何不断提高自己的专业技能

1、一定要看书 现在学习Java变得比以前容易多了&#xff0c;除了有大量的视频教程外&#xff0c;还有专业的java培训机构&#xff0c;这都使学习变得更加傻瓜化&#xff0c;然而我要说的是&#xff0c;Java虽然变得越来越容易学&#xff0c;然而那只代表入门容易&#xff0c;并…

Java学习路线,你值得了解

Java学习路线&#xff0c;了解一下&#xff01; 需要资料的欢迎加入学习交流群&#xff1a;9285&#xff0c;05736

_linux中curl命令详解-linux运维

linux中curl命令的使用详情&#xff1a;1、【curl url】获取该网址的文本信息&#xff1b;2、【curl -i url】获取该网址的文本信息以及协议头部信息&#xff1b;3、【curl -x proxy url】使用代理获取网页文本信息。linux中curl命令的使用详情&#xff1a;1、curl url(获取该网…

Java的重载与覆盖,傻傻分不清!

重载和覆盖是JAVA多态性的不同表现方式&#xff0c;其中重载是在一个类中多态性的一种表现&#xff0c;是指在一个类中定义了多个同名的方法&#xff0c;他们或有不同参数个数或有不同参数类型。 在使用重载时需注意以下几点&#xff1a; 1&#xff09;重载是通过参数来区分的…

python字符串_教你快速了解 Python 字符串

我们知道字符串是 Python 中最常用的数据类型。我们可以使用引号(或")来创建字符串。创建字符串很简单&#xff0c;只要为变量分配一个值即可。例如&#xff1a;var1 Hello World! var2 "Python Runoob"Python 访问字符串中的值Python 不支持单字符类型&#…

那些年踩过的Java异常,简直了!

1、NullPointerException 空指针异常&#xff0c;操作一个 null 对象的方法或属性时会抛出这个异常。具体看这篇文章&#xff1a;Java 避免空指针的 5 个案例。 2、OutOfMemoryError 内存异常异常&#xff0c;这不是程序能控制的&#xff0c;是指要分配的对象的内存超出了当前最…