Java之Socket开发

来源:互联网 发布:淘宝尺码助手在哪 编辑:程序博客网 时间:2024/05/12 05:11


通信过程

1.找到对方IP地址。

2.数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。这个数字标识我们称之为端口,这是与物理端口相区别的逻辑端口。

3.定义通信规则,也就是通讯协议,如国际组织定义了通用协议TCP/IP。特有的组织和单位为了通讯安全会使用一套自己的协议。如果发送方和接收方使用同一套通信规则,那么发送方的数据接收方无法解析和理解(接收方看不懂)。


IP和端口

IP:

IP4分为四段,每段的大小为一个字节,本机在没有配置任何IP地址时默认本机地址为127.0.0.1,如果cmd中使用ping命令查看127.0.0.1出现问题的话,那就是网卡出现了问题,有些IP地址段作为保留段只留给局域网使用,如192.168。简而言之,IP就只计算机的生份证号码,全球唯一,在通信中利用IP可以找到这台计算机。

端口:

0-65535之间,0一般不用,用作自动分配可用端口。简而言之,端口就是一台计算机中每个软件的身份证号码,在这台电脑中唯一,在通信中利用端口可以在一台计算机中找到一个软件。


计算机网络通信-OSI参考模型

七层模型中每一层数据都会经历装包和拆包的过程。每一层都有自己层对应的协议。

应用层 (Application): 网络服务与最终用户的一个接口。协议有:HTTP FTP TFTP SMTP SNMP DNS

表示层(Presentation Layer):数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层)

会话层(Session Layer):建立、管理、终止会话。(在五层模型里面已经合并到了应用层)

传输层 (Transport):定义传输数据的协议端口号,以及流控和差错效验。 协议有:TCP UDP

网络层 (Network):进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

数据链路层 (Link):建立逻辑连接、进行硬件地址寻址、差错效验等功能。(由底层网络定义协议)

物理层(Physical Layer):建立、维护、断开物理连接。(由底层网络定义协议)



应用层之间的协议通过逐级调用传输层(Transport layer)、网络层(Network Layer)和物理数据链路层(Physical Data Link)而可以实现应用层的应用程序通信互联。

应用层需要关心应用程序的逻辑细节,而不是数据在网络中的传输活动。应用层其下三层则处理真正的通信细节。

在 Internet 整个发展过程中的所有思想和着重点都以一种称为 RFC(Request For Comments)的文档格式存在。针对每一种特定的 TCP/IP 应用,有相应的 RFC 文档。

一些典型的 TCP/IP 应用有 FTP、Telnet、SMTP、SNTP、REXEC、TFTP、LPD、SNMP、NFS、INETD 等。RFC 使一些基本相同的 TCP/IP 应用程序实现了标准化,从而使得不同厂家开发的应用程序可以互相通信。


计算机网络通信-TCP/IP参考模型

TCP/IP是一组用于实现网络互连的通信协议。Internet网络体系结构以TCP/IP为核心。基于TCP/IP的参考模型将协议分成四个层次,它们分别是:网络访问层、网际互联层、传输层(主机到主机)、和应用层。


1. 应用层
应用层对应于OSI参考模型的高层,为用户提供所需要的各种服务,例如:FTP、Telnet、DNS、SMTP等。

2. 传输层
传输层对应于OSI参考模型的传输层,为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性。

该层定义了两个主要的协议:传输控制协议(TCP)和用户数据报协议(UDP)。

TCP协议提供的是一种可靠的、通过“三次握手”来连接的数据传输服务;而UDP协议提供的则是不保证可靠的(并不是不可靠)、无连接的数据传输服务.

3. 网际互联层
网际互联层对应于OSI参考模型的网络层,主要解决主机到主机的通信问题。它所包含的协议设计数据包在整个网络上的逻辑传输。

注重重新赋予主机一个IP地址来完成对主机的寻址,它还负责数据包在多种网络中的路由。

该层有三个主要协议:网际协议(IP)、互联网组管理协议(IGMP)和互联网控制报文协议(ICMP)。
IP协议是网际互联层最重要的协议,它提供的是一个可靠、无连接的数据报传递服务。

4. 网络接入层(即主机-网络层)
网络接入层与OSI参考模型中的物理层和数据链路层相对应。它负责监视数据在主机和网络之间的交换。

事实上,TCP/IP本身并未定义该层的协议,而由参与互连的各网络使用自己的物理层和数据链路层协议,然后与TCP/IP的网络接入层进行连接。

地址解析协议(ARP)工作在此层,即OSI参考模型的数据链路层。


TCP协议

TCP协议简介

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在简化的计算机网络OSI模型(TCP/IP模型)中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。

在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。

应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分区成适当长度的报文段(通常受该计算机连接的网络的数据链路层的最大传输单元的限制),之后TCP把结果包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。

TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收,然后接收端实体对已成功收到的包发回一个相应的确认(ACK)。
如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。

TCP协议的优点

TCP是安全和可靠的,TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。很多网络连接和网络协议都是基于TCP的,如HTTP、FTP、各种邮箱协议等。

UDP协议

UDP协议简介

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务 ,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。

在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。

UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。

与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。

UDP协议的优点

它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

JavaSocket

Socket-TCP

TCP有服务端和客户端之分,一个ServerSockt就是一个服务端,一个Socket就是一个客户端与服务端之间连接。

ServerSocket类参考手册(API上有):

ServerSockt类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。 
构造方法:
ServerSocket() 创建非绑定服务器套接字。 
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。 
ServerSocket(int port, int backlog) 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 
ServerSocket(int port, int backlog, InetAddress bindAddr) 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 
主要方法:
accept() 侦听并接受到此套接字的连接。
close() 关闭此套接字。

Socket类参考手册(API上有):

此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
构造方法:
  Socket()  通过系统默认类型的 SocketImpl 创建未连接套接字 
  Socket(InetAddress address, int port)  创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 
  Socket(InetAddress address, int port, InetAddress localAddr, int localPort)  创建一个套接字并将其连接到指定远程地址上的指定远程端口。 
  Socket(Proxy proxy)  创建一个未连接的套接字并指定代理类型(如果有),该代理不管其他设置如何都应被使用。 
  Socket(String host, int port)  创建一个流套接字并将其连接到指定主机上的指定端口号。 
  Socket(String host, int port, InetAddress localAddr, int localPort)  创建一个套接字并将其连接到指定远程主机上的指定远程端口。 
主要方法:
getInputStream() 返回此套接字的输入流。
getOutputStream()  返回此套接字的输出流。
close() 关闭此套接字。

Socket-UDP

UDP没有服务端和客户端之分,每一个DatagramSocket既是服务端也是客户端。

DatagramSocket类参考手册(API上有):

DatagramSocket用于接收和发送UDP的Socket实例。
构造方法:
DatagramSocket():通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。程序会让操作系统分配一个可用的端口。
DatagramSocket(int port):创建实例,并固定监听Port端口的报文。通常用于服务端
DatagramSocket(int port, InetAddress localAddr):这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文。
主要方法:
1)receive(DatagramPacket d):接收数据报文到d中。receive方法产生一个“阻塞”。“阻塞”是一个专业名词,它会产生一个内部循环,使程序暂停在这个地方,直到一个条件触发。
2)send(DatagramPacket dp):发送报文dp到目的地。
3)setSoTimeout(int timeout):设置超时时间,单位为毫秒。
4)close():关闭DatagramSocket。在应用程序退出的时候,通常会主动释放资源,关闭Socket,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭Socket,或在捕获到异常抛出后关闭Socket。

代码示例

import java.io.OutputStream;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;public class TCPTest {public static void main(String[] args) {new TCPServer("服务器").start();new TCPClient("客户端1").start();new TCPClient("客户端2").start();}}/** * 服务器线程,不停的读取客户端发送的信息,同时不停的向客户端发送信息 *  * @author Tang *  */class TCPServer extends Thread {private String name;public TCPServer(String name) {this.name = name;}public void run() {try (ServerSocket server = new ServerSocket(10010, 10000000, InetAddress.getLoopbackAddress())) {System.out.println(name + "开始运行...");while (true) {Socket socket = server.accept();System.out.println(name + "已经接收到一个客户端的连接...");new TCPReadThread(name, socket.getInputStream()).start();new TCPWriteThread(name, socket.getOutputStream(), name + "发送的消息").start();}} catch (Exception e) {throw new RuntimeException(e);}}}/** * 客户端线程,不停的读取服务器发送的信息,同时不停的向服务器发送信息 *  * @author Tang *  */class TCPClient extends Thread {private String name;public TCPClient(String name) {this.name = name;}public void run() {try {Socket socket = new Socket(InetAddress.getLoopbackAddress(), 10010);// 不需要关闭,要保持与服务器的连接能一直读写System.out.println(name + "开始运行...");new TCPReadThread(name, socket.getInputStream()).start();new TCPWriteThread(name, socket.getOutputStream(), name + "发送的消息").start();} catch (Exception e) {throw new RuntimeException(e);}}}/** * 负责不停写的线程 *  * @author Tang */class TCPWriteThread extends Thread {private String name;private OutputStream outputStream;private String sendInfo;public TCPWriteThread(String name, OutputStream outputStream, String sendInfo) {super();this.name = name;this.outputStream = outputStream;this.sendInfo = sendInfo;}public void run() {try (OutputStream outputStream = this.outputStream) {System.out.println(name + "开始发送消息");while (true) {outputStream.write(sendInfo.getBytes());outputStream.flush();System.out.println(name + "发送消息:" + sendInfo);System.out.println();Thread.sleep(5000);}} catch (Exception e) {throw new RuntimeException(e);}}}/** * 负责不停读的线程 *  * @author Tang */class TCPReadThread extends Thread {private String name;private InputStream inputStream;public TCPReadThread(String name, InputStream inputStream) {this.name = name;this.inputStream = inputStream;}public void run() {try (InputStream inputStream = this.inputStream) {System.out.println(name + "开始读取消息");while (true) {byte[] bufferBytes = new byte[Math.max(inputStream.available(), 1024)];for (int len = inputStream.read(bufferBytes); len > 0; len = inputStream.read(bufferBytes)) {System.out.println(name + "收到消息:" + new String(bufferBytes, 0, len));}}} catch (Exception e) {throw new RuntimeException(e);}}}


DatagramPacket类参考手册(API上有):

DatagramPacket用于处理报文,它将Byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成Byte数组。应用程序在产生数据包是应该注意,TCP/IP规定数据报文大小最多包含65507个,通常主机接收548个字节,但大多数平台能够支持8192字节大小的报文。
构造方法:
DatagramPacket(byte[] buf, int length):将数据包中Length长的数据装进Buf数组,一般用来接收客户端发送的数据。
DatagramPacket(byte[] buf, int offset, int length):将数据包中从Offset开始、Length长的数据装进Buf数组。
DatagramPacket(byte[] buf, int length, InetAddress clientAddress, int clientPort):从Buf数组中,取出Length长的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。
DatagramPacket(byte[] buf, int offset, int length, InetAddress clientAddress, int clientPort):从Buf数组中,取出Offset开始的、Length长的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。
主要方法:
1)getData(): 从实例中取得报文的Byte数组编码。
2)setDate(byte[] buf):将byte数组放入要发送的报文中。

代码示例

import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class UDPTest {public static void main(String[] args) {new UDPClient1().start();new UDPClient2().start();}}/** * 本客户端绑定10010端口,不停的发消息给10011端口,再等10011端口反馈消息给自己,并打印10011端口反馈的消息 *  * @author Tang */class UDPClient1 extends Thread {public void run() {try (DatagramSocket datagramSocket = new DatagramSocket(10010, InetAddress.getLoopbackAddress())) {while (true) {String sendInfo = ("我要发消息给你,10011美女,约吗?");DatagramPacket sendPacket = new DatagramPacket(sendInfo.getBytes(), sendInfo.length(), InetAddress.getLoopbackAddress(), 10011);datagramSocket.send(sendPacket);System.out.println("UDPClient1已经发出消息:" + sendInfo);byte[] buffer = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);datagramSocket.receive(receivePacket);System.out.println("UDPClient1收到地址为:" + receivePacket.getAddress().getHostAddress() + "端口号为" + receivePacket.getPort() + "发来的消息:"+ new String(receivePacket.getData(), 0, receivePacket.getLength()));System.out.println();Thread.sleep(2000);}} catch (Exception e) {throw new RuntimeException(e);}}}/** * 本客户端绑定10011端口,不停的从10010端口接受信息,并打印接受到的消息,再发送反馈消息给10010端口。 *  * @author Tang */class UDPClient2 extends Thread {public void run() {try (DatagramSocket datagramSocket = new DatagramSocket(10011, InetAddress.getLoopbackAddress())) {while (true) {byte[] buffer = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);datagramSocket.receive(receivePacket);System.out.println("UDPClient2收到地址为:" + receivePacket.getAddress().getHostAddress() + "端口号为" + receivePacket.getPort() + "发来的消息:"+ new String(receivePacket.getData(), 0, receivePacket.getLength()));String sendInfo = ("我要反馈点消息给你," + receivePacket.getPort() + "你这个流氓,居然不发时间地点怎么约?");DatagramPacket sendPacket = new DatagramPacket(sendInfo.getBytes(), sendInfo.length(), receivePacket.getAddress(), receivePacket.getPort());datagramSocket.send(sendPacket);System.out.println("UDPClient2已经发出消息:" + sendInfo);}} catch (Exception e) {throw new RuntimeException(e);}}}


参考资料:http://baike.baidu.com/link?url=5KTeHj5AfeopauKC82U0Qsh9gE0aa_gXO-GFUqx3fGt3Fc3ASjbGG_8VtPlEsVAtF-E7gGA8PJ-uQLZMHCA55a

http://baike.baidu.com/link?url=G11LQnFqZP3MRiNese6QxFe8J0OGq5Qh30N4HYwQBYrruM1-BWuj8R0Tc77STVtx1x-AVQjeRg4NHa1yE2kQPq



0 0
原创粉丝点击