Pre-dinnerDessertUnix的输入输出(IO)系统遵循Open-Read-Write-Close的操作范式。用户进程在进行IO操作前,需要调用Open指定并获取对要操作的文件或设备的读写权限。一旦IO操作对象被打开,用户进程就可以对该对象执行一个或多个读或写操作。Read操作用于从IO操作对象中读取数据,并将数据传递给用户进程。Write操作用于将用户进程中的数据传输(写入)到IO操作对象中。当所有的Read和Write操作结束后,用户进程需要调用Close来通知系统它已经完成了对IO对象的使用。当Unix开始支持进程间通信(InterProcessCommunication,简称IPC)时,IPC的接口被设计成类似于文件IO操作接口。在Unix中,一个进程有一组可以读写的IO描述符。IO描述符可以是文件、设备或通信通道(套接字)。文件描述符由三部分组成:创建(打开套接字)、读写数据(接受和发送到套接字)和销毁(关闭套接字)。在Unix系统上,类BSD版本的IPC接口被实现为TCP和UDP协议之上的一层。消息的目的地由套接字地址指示。套接字地址是由网络地址和端口号组成的通信标识符。进程间通信操作需要一对套接字。进程间通信是通过一个进程中的套接字与另一个进程中的另一个套接字之间的数据传输来实现的。当一个消息被执行和发送时,消息在发送端的套接字中排队,直到底层网络协议将这些消息发送出去。当消息到达接收端的套接字时,它也会处于排队状态,直到接收端的进程接收并处理该消息。TCP和UDP通讯关于socket编程,我们有两种通讯协议可以选择。一种是数据报通信,另一种是流通信。数据报通信数据报通信协议就是我们常说的UDP(UserDataProtocol用户数据报协议)。UDP是一个无连接的协议,也就是说我们每发送一个数据报,都需要同时发送本机的socket描述符和接收端的socket描述符。因此,我们每次通信都需要发送额外的数据。流通信流通信协议也称为TCP(TransferControlProtocol,传输控制协议)。与UDP不同,TCP是一种基于连接的协议。在使用流通信之前,我们必须在一对套接字之间建立连接,以进行通信。其中一个套接字充当服务器来侦听连接请求。另一个作为客户端发出连接请求。一旦两个套接字连接起来,它们就可以在一个或两个方向上传输数据。读到这里,我们或多或少都有这样的疑惑,我们是用UDP还是TCP来进行socket编程。选择基于哪种协议进行套接字编程取决于你具体的客户端-服务器应用场景。下面简单分析一下TCP和UDP协议的区别,或许可以帮助大家更好地选择使用哪一种。在UDP中,每次发送数据报时,都需要附加本机的套接字描述符和接收端的套接字描述符。并且因为TCP是一个基于连接的协议,socket对之间在通信之前需要先建立连接才能进行通信,所以在TCP协议中就会存在建立连接需要时间的socket编程。在UDP中,数据报数据的大小限制为64KB。TCP中没有这样的限制。一旦TCP通信的socket对连接起来,它们之间的通信就类似于IO流,所有的数据都会按照收到的先后顺序被读取。UDP是一个不可靠的协议,发送的数据报可能不会按照发送的先后顺序被接收端的socket接受。那么TCP就是一个可靠的协议。接收方收到的数据包的顺序与发送方收到的数据包的顺序相同。总之,TCP适用于远程登录(rlogin、telnet)、文件传输(FTP)等网络服务。因为需要传输的数据大小是不确定的。UDP比TCP更简单、更轻便。UDP用于实现一些实时性高或丢包不重要的业务。UDP在局域网中的丢包率比较低。#p#Java中的Socket编程下面我将通过一些例子来说明如何使用socket来编写客户端和服务端程序。注意:在下面的例子中,我将使用基于TCP/IP协议的socket编程,因为这个协议的应用远比UDP/IP广泛。而所有socket相关的类都位于java.net包下,所以我们在进行socket编程的时候需要引入这个包。在客户端编写打开Socket如果是在客户端,需要编写如下代码打开一个socket。字符串主机=“127.0.0.1”;内部端口=8919;套接字客户端=新套接字(主机,端口);上面代码中,host是客户端需要连接的机器,port是服务端用来监听请求的端口。选择端口时需要注意0~1023端口已经被系统预留。这些端口被一些常用服务使用,例如邮件、FTP和HTTP。在写服务端代码和选择端口时,请选择大于1023的端口。写数据接下来就是写请求数据了。我们从客户端的socket对象中获取OutputStream对象,然后写入数据。很像文件IO的处理代码。公共类ClientSocket{publicstaticvoidmain(Stringargs){Stringhost="127.0.0.1";内部端口=8919;尝试{Socketclient=newSocket(host,port);Writerwriter=newOutputStreamWriter(client.getOutputStream);writer.write("来自客户的问候");writer.flush;writer.close;客户端关闭;}catch(IOExceptione){e.printStackTrace;}}}关闭IO对象类似于文件IO,在读写数据之后,最后我们需要关闭IO对象,以保证资源的正确释放。在server端写打开server端socketintport=8919;ServerSocketserver=newServerSocket(端口);套接字socket=server.accept;上面的代码创建了一个服务器端的socket,然后调用accept方法监听并获取客户端的请求socket。accept方法是一个阻塞方法,等待直到服务器和客户端之间的连接建立。读取数据通过上面获取的socket对象获取InputStream对象,然后安装文件IO读取数据。这里我们打印出内容。公共类ServerClient{publicstaticvoidmain(Stringargs){intport=8919;尝试{ServerSocketserver=newServerSocket(port);套接字socket=server.accept;Readerreader=newInputStreamReader(socket.getInputStream);charchars=newchar[1024];国际长度;StringBuilderbuilder=newStringBuilder;while((len=reader.read(chars))!=-1){builder.append(newString(chars,0,len));}系统。out.println("收到来自客户端的消息=:"+builder);阅读器关闭;套接字关闭;服务器关闭;}catch(Exceptione){e.printStackTrace;}}}别忘了关闭IO对象,***需要正确关闭IO对象才能保证资源的正确释放。#p#注意一个例子这里我们添加一个例子,使用socket实现一个回显服务器,即服务器将客户端发送的数据返回给客户端。代码非常简单。导入java.io.*;导入java.net.*;publicclassEchoServer{publicstaticvoidmain(Stringargs){//声明部分://为服务器声明一个服务器套接字和一个客户端套接字//声明一个输入和输出流ServerSocketechoServer=null;弦线;数据输入流是;打印流操作系统;套接字clientSocket=null;//尝试在端口9999上打开服务器套接字//请注意,如果我们不是特权用户(root),我们不能选择小于1023的端口try{echoServer=newServerSocket(9999);}catch(IOExceptione){System.out.println(e);}//从ServerSocket创建一个套接字对象来侦听和接受//连接。//打开输入输出流try{clientSocket=echoServer.accept;is=newDataInputStream(clientSocket.getInputStream);os=newPrintStream(clientSocket.getOutputStream);//只要我们收到数据,就将该数据回显给客户端。while(true){line=is.readLine;os.println(li无);}}catch(IOExceptione){System.out.println(e);}}}编译运行上面的代码,发出如下请求,可以看到客户端请求携带的数据内容15:00$curlhttp://127.0.0.1:9999/?111GET/?111HTTP/1.1User-Agent:curl/7.37.1Host:127.0.0.1:9999Accept:*/*客户端-服务器编程或比较的总结有趣的是,同时Java中的套接字编程比C等其他语言。java.net包中包含许多功能强大且灵活的类,供开发人员进行网络编程。在网络编程中,推荐使用该包下的API。同时Sun.*包中也包含了很多网络编程相关的类,但是不推荐使用这个包下的API,因为这个包可能会变,不能保证这个包在所有平台上都包含.
