做网站维护工资多少/semester
Socket基础知识
Socket(套接字)用于描述IP地址和端口,应用程序可以通过Socket向网络发出请求或者应答网络请求。
Socket是支持TCP/IP协议的网络通信的基本操作单元,是对网络通信过程中端点的抽象表示,包含了进行网络通信所必需的5种信息:连接所使用的协议、本地主机的IP地址、本地进程的协议端口、远地主机的IP地址以及远地进程的协议端口。
Socket的传输模式
Socket有两种主要的操作方式:面向连接(TCP)的和无连接的(UDP)
面向连接的Socket操作就像一部电话,Socket必须在发送数据之前与目的地的Socket取得连接,一旦连接建立了,Socket就可以使用一个流接口进行打开、读写以及关闭操作。并且,所有发送的数据在另一端都会以相同的顺序被接收。
无连接的Socket操作就像一个邮件投递,每一个数据报都是一个独立的单元,它包含了这次投递的所有信息(目的地址和要发送的内容)。在这个模式下的Socket不需要连接目的地Socket,它只是简单的投出数据报。
由此可见,无连接的操作是快速高效的,但是数据安全性不佳;面向连接的操作效率较低,但数据的安全性较好。
本文主要介绍的是面向连接的Socket操作。
Socket的传输模式简单经典实例
面向连接的Socket主要用到两个类:服务器端:ServerSocket;客户端:Socket。
第一例:Socket实现客户端向服务器端通信
实现客户端向服务器端的通信只需要启动一个服务器端用来监听客户端发送的消息,然后建立客户端和服务器端的连接,这时客户端想服务器端发送信息就可以接收到了,这也是一个最简单的例子。
SocketServeice(服务器):
package socket.simple;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class SocketService {//搭建服务器端public static void main(String[] args) throws IOException{//创建服务器ServerSocket server=new ServerSocket(5209);System.out.println("服务器启动成功");//等待客户端连接后,接收客户端socketSocket socket=server.accept();//获取客户端socket的输入流BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));while(true){//等待客户端socket的不为空输入流String str = in.readLine();if (str == null) {break;}System.out.println("客户端说:" + str);}in.close(); //关闭Socket输入流socket.close(); //关闭Socketserver.close(); //关闭ServerSocket}
}
SocketClient(客户端):
package socket.simple;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;public class SocketClient {// 搭建客户端public static void main(String[] args) throws IOException {//创建客户端,并连接服务器Socket socket = new Socket("192.168.10.2", 5209);System.out.println("客户端启动成功");//获取控制台输入流BufferedReader out = new BufferedReader(new InputStreamReader(System.in));//通过socket输出流创建write推送功能对象PrintWriter pw = new PrintWriter(socket.getOutputStream());while (true) {//等待控制台不为空的输入流String str = out.readLine();if ("".equals(str)) {break;}//通过socket对象将字符串推送到服务器pw.println(str);//立刻刷新推送功能对象pw.flush();} // 继续循环pw.close(); // 关闭Socket输出流socket.close(); // 关闭Socket}}
接下来我们分析一下上面的代码,首先给大家普及两个概念。
第一个是阻塞函数,阻塞函数是当这个函数不执行完,函数所在线程就一直停止在这里不动。
上面的例子中,涉及到了两个阻塞函数,一个是SocketServer.accept(),一个是BufferedReader.readline()。当没有数据读取时,就一直会阻塞在那。也就是说如果这两个方法获取不到数据的话,程序就停在这了,不再继续往下走了。
第二个是两个在Socket例子中常用类,BufferedReader和PrintWriter。BufferedReader是用来读取输入流的,包括服务器接收客户端推送过来,或者客户端接收服务器推送过来的数据流和控制台输入的数据流。PrintWriter是用来推送输出流的,可以将数据流从客户端推送给服务器,或者从服务器推送给客户端。
简单的来说,BufferedReader是接收InputStream;PrintWriter是发送OutputStream。
它们的实现方式分别是:
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
br.readline();
和
PrintWriter pw = new PrintWriter(outputStream);
pw.println(str);
pw.flush;
需要注意的是上面的br.readline()和pw.println(str)是一起使用的,因为readline()读入的数据时必须要有/r或/n或/r/n,也就是必须要有换行符,否则会为了等待一个换行/回车符而一直阻塞,所以必须使用println()方法。
理解了上面的两个概念后,再看上面的例子,我们来分析下代码逻辑。
首先我们是先启动服务器,服务器启动后,accept方法会阻塞等待一个socket连接,服务器的代码走到这就停了。
然后我们又启动了客户端。客户端启动后,服务器会往下走,走到循环中的readline方法,阻塞等待获取的客户端socket中输入流的出现,而同时,客户端也会被循环中的readline方法阻塞,等待控制台中的输入流的出现,整个项目就进入了等待中。
这时,我们在客户端的控制台中输入一串字符,客户端中的readline就会立刻读取到这段输入流,然后客户端通过PrintWriter将该输入流通过socket的输出流推送到服务器的socket中,然后进入下一个循环的等待。而服务器中的readline就会立刻读取到socket的输入流,然后打印到服务器的控制台,之后又会进入下一个循环的等待。
理解了逻辑后不难看出,在这个例子中,之所以使用while(true)是为了能够一直循环的进入下一轮的输入输出,如果去掉循环,那么就只能实现一次数据的通信。
第二例:Socket实现客户端与服务器端通信
在第一个例子中,只是实现了客户端向服务器发送信息,但服务器并没有对客户端进行信息回复。所以第二个例子实现双向通信的。为了简化代码,提高可读性,接下来的例子我将不再进行资源的关闭回收。
SocketService(服务器):
package socket.both;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class SocketService {//搭建服务器端public static void main(String[] args) throws IOException{ServerSocket server=new ServerSocket(5209);System.out.println("服务器启动成功");Socket socket=server.accept();BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));BufferedReader out=new BufferedReader(new InputStreamReader(System.in));PrintWriter pw = new PrintWriter(socket.getOutputStream());while(true){System.out.println("客户端说:"+in.readLine());String str = out.readLine();pw.println(str);pw.flush();System.out.println("服务器说:"+str);}}
}
SocketClient(客户端):
package socket.both;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;public class SocketClient {// 搭建客户端public static void main(String[] args) throws IOException {Socket socket = new Socket("192.168.10.2", 5209);System.out.println("客户端启动成功");BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));BufferedReader out = new BufferedReader(new InputStreamReader(System.in));PrintWriter pw = new PrintWriter(socket.getOutputStream());while (true) {String str = out.readLine();pw.println(str);pw.flush();System.out.println("客户端说:" + str);System.out.println("服务器说:"+in.readLine());}}}
第二个例子可以非常好的体现出面向连接的Socket发送的数据在另一端会以相同的顺序被接收这个特点。接下来,我们来分析下代码逻辑。
首先我们先启动服务器,然后启动客户端,这时服务器会走到等待客户端发送socket中输入流的步骤(in.readLine()),客户端会走到等待控制台中输入流的步骤(out.readLine())。
然后因为客户端在等待控制台输入,服务器在等待客户端发送信息,所以我们先在客户端的控制台输入一串字符,这时,客户端就会在控制台打印输出这串字符,然后走到等待服务器发送socket中输入流的步骤(in.readLine()),服务器就会收到这串输入流,然后在控制台打印输出这串输入流,然后走到等待控制台中输入流的步骤(out.readLine())。
之后再服务器端的控制台输入一串字符,又会进入下一个循环的等待。
这个例子的逻辑是客户端先发送一次通信,服务器接收通信并发送一次通信,客户端再接收并发送一次通信。之前也说了,由于面向连接的Socket发送的数据在另一端会以相同的顺序被接收,所以,如果你在客户端连续发送了两次通信,那么服务器一个循环只会接收一次通信,然后服务器再次发送一次通信后,才会接收到客户端的第二次通信。理由是下面的readline已经读到数据可以继续执行了,但上面的readline还没有读到数据,程序在这就已经阻塞了。
这个特点会导致一个现象,就是客户端发送几十几百次消息,但服务器只接收到了第一个消息。其实是剩下的消息还没有进行读取而已。
以上两个例子是为了帮助你更好的理解Socket应该怎么使用,但是只是一对一的通信,而且是服务器对客户端的一对一通信,那么多个客户端怎么连接服务器呢?客户端和客户端之间怎么通信呢?这就是Socket的聊天室功能了。
有兴趣的人可以参考:http://blog.csdn.net/qq_33865313/article/details/79363640
如果你还有其他的看法或者想法,欢迎在评论区留下你的意见,大家一起讨论,一起进步,做一只有梦想的网虫!