文档章节

Java网络编程——8.客户端Socket

天子剑毅
 天子剑毅
发布于 2017/04/26 15:49
字数 1948
阅读 37
收藏 1

在Internet上,数据按有限大小的包传输,这些包称为数据报(datagram)。每个数据报包含一个首部(header)和一个有效载荷(payload)。首部包含包发送到的地址和端口、包来自的地址和端口、检测数据是否破坏的校验和,以及用于保证可靠传输的各种其他管理信息。Socket允许程序员将网络连接看作是另外一个可以读/写字节的流,它对程序员掩盖了网络的底层细节,如错误检测、包大小、包分解、包重传、网络地址等。

1、使用Socket

Socket是两台主机之间的一个连接,它可以完成7个基本操作:

  • 连接远程机器
  • 发送数据
  • 接收数据
  • 关闭连接
  • 绑定端口
  • 监听入站数据
  • 在绑定端口上接受来自远程机器的连接。

Java的Socket类提供了客户端和服务器都能操作的前4个方法,后面三个方法由ServerSocket类实现,仅服务器需要。一旦建立了socket连接,本地和远程主机就从这个socket得到输入和输出流,两台主机可以同时发送和接收数据。数据的含义取决于协议,发送给FTP服务器的命令与发送给HTTP服务器的命令就有所不同,一般先完成某种协商握手,然后再具体传输数据。

2、用Telnet研究协议

Socket本身非常简单,不过与不同服务器通信的协议会使工作变得复杂。为了对协议如何操作有所认识,可以使用Telnet连接一个服务器,输入不同的命令并观察它的响应。

**$    telnet 121.40.47.132 13**
Trying 121.40.47.132...
Connected to 121.40.47.132.
Escape character is '^]'.
25 APR 2017 18:04:20 CST
Connection closed by foreign host.

“25 APR 2017 18:04:20 CST”行是daytime服务器发送的时间。读取这个Socket的InputStream时就会得到这个结果,其他各行内容由unix shell或Telnet程序生成。

下面来看如何通过编程使用socket获取同样的数据,在Java 7中,Socket实现了Autocloseable:

        try(Socket socket=new Socket("121.40.47.132",13)) {
            socket.setSoTimeout(10000);//10秒超时
            
            InputStream inputStream=socket.getInputStream();
            StringBuilder time=new StringBuilder();
            InputStreamReader reader=new InputStreamReader(inputStream);
            for(int c=reader.read();c!=-1;c=reader.read()){
                time.append((char)c);
            }
            System.out.println(time);
        } catch (IOException e) {
            e.printStackTrace();
        }

对网络代码而言这就足够了,不过大多数类似的网络程序中,重点工作通常是使用协议和理解数据格式。例如,你可以把服务器发送给你的文本解析为一个java.util.Date对象。并不是所有协议都使用ASCII编码,甚至不一定使用文本。

用Socket还可以写入服务器。dict是一个简单的双向TCP,客户端向dict服务器打开一个socket,并发送类似“STATUS”的命令,服务器会返回相应的结果。使用unix telnet如下所示:

**$    telnet dict.org 2628**
Trying 216.18.20.172...
Connected to dict.org.
Escape character is '^]'.
220 pan.alephnull.com dictd 1.12.1/rf on Linux 4.4.0-1-amd64 <auth.mime> <43418892.5812.1493185426@pan.alephnull.com>
STATUS
210 status [d/m/c = 0/0/0; 3.000r 0.000u 0.000s]
Q
221 bye [d/m/c = 0/0/0; 25.000r 0.000u 0.000s]
Connection closed by foreign host.

用Java实现这个协议并不难:

        Socket socket = null;
        try {
            socket = new Socket("dict.org", 2628);
            socket.setSoTimeout(10000);// 10秒超时

            OutputStream outputStream = socket.getOutputStream();
            Writer writer = new OutputStreamWriter(outputStream);
            writer.write("STATUS\r\n");
            writer.flush();// 刷新输出

            InputStream inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            for (String line = reader.readLine(); !line.equals("."); line = reader.readLine()) {
                System.out.println(line);
            }

            writer.write("QUIT\r\n");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                }
            }
        }

close()方法同时关闭Socket的输入和输出,有时你可能希望只关闭连接的一半,即输入或者输出。shutdownInput()和shutdownOutput()方法只关闭连接的一半,这并不关闭socket,实际上它会调整与Socket连接的流,使它认为已经到了流的末尾。关闭输入之后再读取输入流会返回-1,关闭输出之后再写入Socket则会抛出一个IOException。shutdown只影响Socket流,并不释放Socket关联的资源,如占用的端口等,所以使用结束后仍需要关闭该Socket。

3、构造和连接Socket

java.net.Socket类是Java完成客户端TCP操作的基础类,其他建立TCP网络连接的面向客户端的类(如URL、URLConnection、Applet)最终都会调用这个类的方法。这个类本身使用原生代码与主机操作系统的本地TCP栈进行通信。

每个Socket构造函数指定要连接的主机和端口,在构造函数返回之前,会与远程主机建立一个活动的网络连接。如果没有为Socket构造函数提供任何参数,它就没有目标主机可以连接,可以后再为某个connect()方法传入一个SocketAddress来建立连接。

SocketAddress类表示一个连接端点,主要用途是为暂时的socket连接信息(如IP和端口)提供一个方便的存储,即时最初的socket已断开并被垃圾回收,这些信息也可以重用来创建新的Socket。Socket类提供了两个返回SocketAddress对象的方法(getRemoteSocketAddress()返回所连接系统的地址,getLoaclSocketAddress()返回发起连接的地址)。

Socket对象有一些属性可以通过获取方法来访问。getInetAddress()和getPort()方法指出Socket连接到的远程主机和端口,getLocalAddress()和getLocalPort()方法指出socket从哪个网络接口和端口连接。

4、设置Socket选项

Socket选项指定了Java Socket类所依赖的原生socket如何发送和接收数据。对于客户端Socket,Java支持9个选项:

  • TCP_NODELAY:设置为true可确保包会尽可能快地发送,无论包的大小,这样可以打破缓存模式,所有包一旦就绪就会发送。
  • SO_BINDADDR:设置为true则允许另一个Socket绑定到这个端口。
  • SO_TIMEOUT:设置这个选项确保read()调用阻塞的时间不会超过某个固定的毫秒数。
  • SO_LINGER:如果这个选项打开,socket close()方法会阻塞指定秒数(如果延迟时间设置为正数),等待发送数据和接收确认,Socket 关闭后,所有剩余的数据都不会发送,也不会收到确认。
  • SO_SNDBUF:控制用于网络输入的建议的发送缓冲区大小。
  • SO_RCVBUF:控制用于网络输入的建议的接收缓冲区大小。
  • SO_KEEPALIVE:如果打开这个选项,客户端偶尔会通过一个空闲连接发送一个数据包,以确保服务器未崩溃。如果在12分钟内未收到响应,客户端就关闭socket。
  • OOBINLINE:如果你希望接收正常数据中的紧急数据,就需要设置为true。
  • IP_TOS:服务类型。

这些选项的滑稽名字来自Berkeley UNIX所使用的C头文件中的命名常量,Socket就是Berkeley UNIX发明的。

5、Socket异常

Socket类的大多数方法都声明抛出IOException或其子类java.net.SocketException。SocketException有几个子类,可以对出现声明问题以及为什么会出问题提供有关的更多信息。如果试图在一个正在使用的端口上构造Socket或ServerSocket对象,或者你没有足够的权限使用这个端口,就会抛出BindException异常。当连接被远程主机拒绝,而拒绝的原因通常是由于主机忙或没有进程在监听该端口,会抛出ConnectException异常。NoRouteToHostException异常表示连接已经超时。当从网络接收的数据违反TCP/IP规范时,会抛出ProtocolException异常。利用这些子类,可以提供信息量更大的错误消息,或者可以确定重试此次失败的操作是否有可能成功。

© 著作权归作者所有

共有 人打赏支持
天子剑毅
粉丝 2
博文 34
码字总数 40324
作品 0
杭州
架构师
私信 提问
Qzone 微信 Java高级——dubbo源码分析之远程通信 netty

Java高级——dubbo源码分析之远程通信 netty dubbo 底层通信选择了 netty 这个 nio 框架做为默认的网络通信框架并且通过自定义协议进行通信。dubbo 支持以下网络通信框架: Netty(默认) Min...

Java架构师那些事
2018/08/29
0
0
基于tcp和udp的socket实现

本文介绍如何用Java实现Socket编程。首先介绍Java针对Socket编程提供的类,以及它们之间的关系。然后分别针对TCP和UDP两种传输层协议实现Socket编程。 1 Java中的Socket编程接口介绍 Java为S...

chjuaner
2017/11/07
0
0
java基础专栏—java网络编程

java网络编程 在网络成层中TCP对应于四层 通常是对网络连接设备的驱动协议,例如对光纤和都对网线的驱动不一样 TCP/IP协议的核心,他用于将数据分组 包括TCP,UDP 主要是负责应用程序的协议。...

T-newcomer
2017/10/25
0
0
请教一个熟悉Java和MySQL的女生如何规划职业生涯?

本来贴的是份简历,然后就改成这样了,我想,首先要做的是明确职业规划,乱来惯了,可能毕不了业,可能毕业了却终身残疾。UC一段乱来的实习经历让我封闭了两个月,于是错过的赤裸裸的就是校招...

何梓
2013/12/17
2.1K
29
分享一个简单易用的RPC开源项目—Tatala

这个项目最早(2008年)是用于一个网络游戏的Cache Server,以及一个电子商务的Web Session服务。后来不断增加新的功能,除了Java还支持C#,到现在已经可以用它来开发网络游戏的服务器。等过些...

zijan
2014/04/08
0
1

没有更多内容

加载失败,请刷新页面

加载更多

取变量的地址赋值给另一个变量,C通过,C++编译出错

取变量的地址赋值给另一个变量,C通过。正常运行,C++编译出错。 代码如下: #include <stdio.h>int main(int argc, char *argv[]){int x = 3;int *p = &x;int y = p;/*c ...

SamXIAO
今天
1
0
利用隐写术实施攻击

尽管隐写术是一种低频攻击途径,但网络犯罪分子已经开始利用它结合社交媒体的普遍性和快速传播性来传递恶意有效负载。 低调但有效的隐写技术虽然是旧把戏,但将代码隐藏在看似正常的图像中,...

Linux就该这么学
今天
4
0
YII2的乐观锁和悲观锁

乐观锁与悲观锁¶ Web应用往往面临多用户环境,这种情况下的并发写入控制, 几乎成为每个开发人员都必须掌握的一项技能。 在并发环境下,有可能会出现脏读(Dirty Read)、不可重复读(Unrep...

echojson
今天
2
0
UCOS线程切换原理

黑客画家
今天
3
0
最牛Java架构师进阶路线(年薪80W)

1、源码分析专题 详细介绍源码中所用到的经典设计思想,看看大牛是如何写代码的,提升技术审美、提高核心竞争力。 帮助大家寻找分析源码的切入点,在思想上来一次巨大的升华。知其然,并知其...

别打我会飞
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部