java socket详解_Java Socket 编程原理及教程「建议收藏」

java socket详解_Java Socket 编程原理及教程「建议收藏」原标题:JavaSocket编程原理及教程对于JavaSocket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了。首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

原标题:Java Socket 编程原理及教程

对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了。首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信。这样就有两个Socket了,客户端和服务端各一个。

对于Socket之间的通信其实很简单,服务端往Socket的输出流里面写东西,客户端就可以通过Socket的输入流读取对应的内容。Socket与Socket之间是双向连通的,所以客户端也可以往对应的Socket输出流里面写东西,然后服务端对应的Socket的输入流就可以读出对应的内容。下面来看一些服务端与客户端通信的例子:

1、客户端写服务端读

服务端代码

821c65638aac18d322a2308dba58e671.png

服务端从Socket的InputStream中读取数据的操作也是阻塞式的,如果从输入流中没有读取到数据程序会一直在那里不动,直到客户端往Socket的输出流中写入了数据,或关闭了Socket的输出流。当然,对于客户端的Socket也是同样如此。在操作完以后,整个程序结束前记得关闭对应的资源,即关闭对应的IO流和Socket。

客户端代码

5ffbc467aad956677cd8c83c465633cc.png

对于客户端往Socket的输出流里面写数据传递给服务端要注意一点,如果写操作之后程序不是对应着输出流的关闭,而是进行其他阻塞式的操作(比如从输入流里面读数据),记住要flush一下,只有这样服务端才能收到客户端发送的数据,否则可能会引起两边无限的互相等待。在稍后讲到客户端和服务端同时读和写的时候会说到这个问题。

2、客户端和服务端同时读和写

前面已经说了Socket之间是双向通信的,它既可以接收数据,同时也可以发送数据。

服务端代码

ccbd89f16b9926fa42570e88379aaceb.png

在上述代码中首先我们从输入流中读取客户端发送过来的数据,接下来我们再往输出流里面写入数据给客户端,接下来关闭对应的资源文件。而实际上上述代码可能并不会按照我们预先设想的方式运行,因为从输入流中读取数据是一个阻塞式操作,在上述的while循环中当读到数据的时候就会执行循环体,否则就会阻塞,这样后面的写操作就永远都执行不了了。除非客户端对应的Socket关闭了阻塞才会停止,while循环也会跳出。针对这种可能永远无法执行下去的情况的解决方法是while循环需要在里面有条件的跳出来,纵观上述代码,在不断变化的也只有取到的长度len和读到的数据了,len已经是不能用的了,唯一能用的就是读到的数据了。针对这种情况,通常我们都会约定一个结束标记,当客户端发送过来的数据包含某个结束标记时就说明当前的数据已经发送完毕了,这个时候我们就可以进行循环的跳出了。那么改进后的代码会是这个样子:

ce0e66e883755f2bb6eaae869e040205.png

在上述代码中,当服务端读取到客户端发送的结束标记,即“eof”时就会结束数据的接收,终止循环,这样后续的代码又可以继续进行了。

客户端代码

731cabcee727caa21e3b52e5b139c640.png

在上述代码中我们先是给服务端发送了一段数据,之后读取服务端返回来的数据,跟之前的服务端一样在读的过程中有可能导致程序一直挂在那里,永远跳不出while循环。这段代码配合服务端的第一段代码就正好让我们分析服务端永远在那里接收数据,永远跳不出while循环,也就没有之后的服务端返回数据给客户端,客户端也就不可能接收到服务端返回的数据。解决方法如服务端第二段代码所示,在客户端发送数据完毕后,往输出流里面写入结束标记告诉服务端数据已经发送完毕了,同样服务端返回数据完毕后也发一个标记告诉客户端。那么修改后的客户端代码就应该是这个样子:

7c71744fafbb3cc83bcee99523288090.png

我们日常使用的比较多的都是这种客户端发送数据给服务端,服务端接收数据后再返回相应的结果给客户端这种形式。只是客户端和服务端之间不再是这种一对一的关系,而是下面要讲到的多个客户端对应同一个服务端的情况。

3、多个客户端连接同一个服务端

像前面讲的两个例子都是服务端接收一个客户端的请求之后就结束了,不能再接收其他客户端的请求了,这往往是不能满足我们的要求的。通常我们会这样做:

d34257f9babdd6a212ba14ee91e0ce00.png

在上面代码中我们用了一个死循环,在循环体里面ServerSocket调用其accept方法试图接收来自客户端的连接请求。当没有接收到请求的时候,程序会在这里阻塞直到接收到来自客户端的连接请求,之后会跟当前建立好连接的客户端进行通信,完了后会接着执行循环体再次尝试接收新的连接请求。这样我们的ServerSocket就能接收来自所有客户端的连接请求了,并且与它们进行通信了。这就实现了一个简单的一个服务端与多个客户端进行通信的模式。

上述例子中虽然实现了一个服务端跟多个客户端进行通信,但是还存在一个问题。在上述例子中,我们的服务端处理客户端的连接请求是同步进行的,每次接收到来自客户端的连接请求后,都要先跟当前的客户端通信完之后才能再处理下一个连接请求。这在并发比较多的情况下会严重影响程序的性能,为此,我们可以把它改为如下这种异步处理与客户端通信的方式:

70f74ecd121ce4b99dfa182e76bb34bb.png

1bdd991ce46110e6cbcd756da63c2549.png

在上面代码中,每次ServerSocket接收到一个新的Socket连接请求后都会新起一个线程来跟当前Socket进行通信,这样就达到了异步处理与客户端Socket进行通信的情况。

在从Socket的InputStream中接收数据时,像上面那样一点点的读就太复杂了,有时候我们就会换成使用BufferedReader来一次读一行,如:

85baa0a424fb6a09d2dd5133c7d973f1.png

2142c82509ee9868edc180bc25262821.png

这个时候需要注意的是,BufferedReader的readLine方法是一次读一行的,这个方法是阻塞的,直到它读到了一行数据为止程序才会继续往下执行,那么readLine什么时候才会读到一行呢?直到程序遇到了换行符或者是对应流的结束符readLine方法才会认为读到了一行,才会结束其阻塞,让程序继续往下执行。所以我们在使用BufferedReader的readLine读取数据的时候一定要记得在对应的输出流里面一定要写入换行符(流结束之后会自动标记为结束,readLine可以识别),写入换行符之后一定记得如果输出流不是马上关闭的情况下记得flush一下,这样数据才会真正的从缓冲区里面写入。对应上面的代码我们的客户端程序应该这样写:

d29b56c0129374c98a96d4b2ab0d4be0.png

4、设置超时时间

假设有这样一种需求,我们的客户端需要通过Socket从服务端获取到XX信息,然后给用户展示在页面上。我们知道Socket在读数据的时候是阻塞式的,如果没有读到数据程序会一直阻塞在那里。在同步请求的时候我们肯定是不能允许这样的情况发生的,这就需要我们在请求达到一定的时间后控制阻塞的中断,让程序得以继续运行。Socket为我们提供了一个setSoTimeout()方法来设置接收数据的超时时间,单位是毫秒。当设置的超时时间大于0,并且超过了这一时间Socket还没有接收到返回的数据的话,Socket就会抛出一个SocketTimeoutException。

假设我们需要控制我们的客户端在开始读取数据10秒后还没有读到数据就中断阻塞的话我们可以这样做:

896eb33311129f25e100eb68591d2bc7.png

5、接收数据乱码

对于这种服务端或客户端接收中文乱码的情况通常是因为数据发送时使用的编码跟接收时候使用的编码不一致。比如有下面这样一段服务端代码:

0e5e3b1f75bb762c4589b3851aa16b92.png

afaf87163b1f8668148f4c3d54a001ee.png

这里用来测试我就弄的混乱了一点。在上面服务端代码中我们在定义输入流的时候明确定义了使用GBK编码来读取数据,而在定义输出流的时候明确指定了将使用UTF-8编码来发送数据。如果客户端上送数据的时候不以GBK编码来发送的话服务端接收的数据就很有可能会乱码;同样如果客户端接收数据的时候不以服务端发送数据的编码,即UTF-8编码来接收数据的话也极有可能会出现数据乱码的情况。所以,对于上述服务端代码,为使我们的程序能够读取对方发送过来的数据,而不出现乱码情况,我们的客户端应该是这样的:

责任编辑:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/180678.html原文链接:https://javaforall.net

(0)
上一篇 2022年10月18日 下午12:16
下一篇 2022年10月18日 下午12:36


相关推荐

  • word 里域代码显示设定

    word 里域代码显示设定1.问题点:WindowsWord里copy&paste时,突然会出现下面的内容。2.解决方案1上述显示的域代码而不是域值。查了一下,可以使用Alt+F9来切换显示域代码&域值。(另:Shift+F9可以切换指定的区域)3.解决方案2在“选项”->”高级”->”显示文档内容”:勾掉”显示域代码而非域值“即可设置

    2022年6月4日
    79
  • 迅雷php文件怎么打开_php生成短链接

    迅雷php文件怎么打开_php生成短链接我正在使用TCPDF来创建简单的pdf文档.我正在创建一个页面并使用下面的代码添加链接$pdf->addTOCPage();$link=$pdf->AddLink();$pdf->SetLink($link,0,-1);现在链接设置成功.但是要导航到该页面我应该添加什么?我试过下面的代码,但它什么也没做,<ahref=“#Whattoaddhere”style…

    2025年10月1日
    3
  • 文件上传linux文件名乱码_java导出文件名中文乱码

    文件上传linux文件名乱码_java导出文件名中文乱码从网上下了一个zip文件,unzip解压后发现中文显示都是乱码,以前也遇到过打开Windows系统下目录文件名也是乱码的问题。这是什么原因呢?文件是在Windows下创建的,而Windows的文件名中文编码默认GBK,Linux中默认文件名编码为UTF-8,编码不一致导致了文件名乱码的问题,解决这个问题需要对文件名进行转码,这个工具就是convmv。SYNOPSIS:convmv…

    2022年8月31日
    6
  • mapminmax的用法

    mapminmax的用法几个要说明的函数接口:[Y,PS]=mapminmax(X)[Y,PS]=mapminmax(X,FP)Y=mapminmax(‘apply’,X,PS)X=mapminmax(‘reverse’,Y,PS)用实例来讲解,测试数据x1=[124],x2=[523];>>[y,ps]=mapminmax(x1)y=  -1

    2022年6月17日
    109
  • C语言 最简分式

    C语言 最简分式Description 我们知道 5 3 1 3 5 称等号后面的式子为最简分式 也就是说 在分式中 分子一定要小于分母 而且分子 分母是互质的 请写一个程序 对于用户输入的一个合法分式 输出其约简之后的结果 Input 输入有多行 每行是 m n 的格式表示的一个分式 其中 m 和 n 都是 int 类型范围内的正整数 输入至 EOF 结束 Output 输出的行数与输入一致 且每行输出与上述输入一一对应 如果约简后 分子部分是 0 则只输出整数部分 如果整数部分是 0 则按照 j k 的格式输出 如果整数部分不

    2026年3月20日
    2
  • android 壁纸尺寸,常见手机壁纸的尺寸是多少

    android 壁纸尺寸,常见手机壁纸的尺寸是多少不同的手机屏幕分辨率不一样,对应的屏幕壁纸尺寸大小也不一样,这里改图宝整理了一些市面上常见的手机型号的壁纸尺寸给大家(单位都为像素):苹果AppleiPhone6:872×1634;iPhone6Plus::2706×2706;iPhone5s:640×1136;iPhone5:640×1136;iPhone8:1334×750;iPhoneX:2436x1125iP…

    2022年6月20日
    141

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号