参考链接:简单理解Socket – 谦行 – 博客园
一、Tcp/Ip
先回顾一下TCP/IP协议
TCP/IP协议是指因特网整个TCP/IP协议族。不同于ISO模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中
应用层:TFTP,HTTP(面向连接),SNMP,FTP,SMTP,DNS,Telnet 等等
传输层:TCP(安全可靠,面向连接,无差错传输),UDP(不可靠的,面向无连接的,传输数据过程中数据可能会丢失,不自动重发看电影 )
网络层(也称为网际层,实现端到端数据分组传输,采用无连接交换方式完成 ):IP,ICMP,OSPF,EIGRP,IGMP
网络接口层:
二、Socket是什么?
我们知道IP层的ip地址可以唯一标识主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。能够唯一标示网络中的进程后,它们就可以利用socket进行通信了
Socket(就是套接字)是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。
套接字用来描述ip地址和端口号,Socket=(IP地址:端口号),套接字的表示方法是点分十进制的lP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。例如:如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)
三、服务器端
第一步:创建一个服务器端,执行ServerSocket.accept()方法,等待客户端连接
第二步:获取客户端传输过来的字节流(socket.getInputStream()),InputStreamReader将字节流转换为字符流,BufferReader从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取,利用bufferReader.readLine()方法循环读取数据
第三步:获取客户端的输出流(socket.getOutputStream()),printWriter.write(str)str是回应给客户端的内容,printWriter.flush(),清空缓存区
第四步:关闭资源
package socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* @Description:服务器端
*/
public class Server {
public static void main( String[] args ) throws IOException {
//第一步:创建一个服务器端,并等待连接客户端
//创造一个服务器端,端口号为81
ServerSocket server=new ServerSocket(8888);
System.out.println("服务器端已启动***************************");
//调用ServerSocket的accept()方法等待连接,该方法会使线程阻塞,
//直到接收到一个客户端的连接
Socket socket= server.accept();
//第二步:获得客户端传过来的信息
//获取客户端传输过来的字节流
InputStream inputStream = socket.getInputStream();
//InputStreamReader 将字节流转换为字符流
//是字节流通向字符流的桥梁,封裝了InputStream在里头, 它以较高级的方式,一次读取一个一个字符,以文本格式输入 / 输出,可以指定编码格式;
InputStreamReader reader = new InputStreamReader(inputStream);
//从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferedReader bufferedReader = new BufferedReader(reader);
//BufferedReader 由Reader类扩展而来,提供通用的缓冲方式文本读取,而且提供了很实用的readLine,
// 读取一个文本行,从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
String info=null;
//利用bufferReader.readLine()方法循环读取数据
while((info=bufferedReader.readLine())!=null)
{
System.out.println("客户端说:"+info);
}
/*虽然在大多数的时候可以直接使用Socket类或输入输出流的close方法关闭网络连接
,但有时我们只希望关闭OutputStream或InputStream,而在关闭输入输出流的同时,并不关闭网络连接。
这就需要用到Socket类的另外两个方法:shutdownInput和shutdownOutput,
这两个方法只关闭相应的输入、输出流,而它们并没有同时关闭网络连接的功能。
和isClosed、isConnected方法一样,Socket类也提供了两个方法来判断Socket对象的输入、输出流是否被关闭,
这两个方法是isInputShutdown()和isOutputShutdown()。*/
socket.shutdownInput();
//第三步:获取客户端的输出流,回应给客户
OutputStream outputStream = socket.getOutputStream();
// 具有自动行刷新的缓冲字符输出流,特点是可以按行写出字符串,并且可以自动行刷新。
PrintWriter printWriter = new PrintWriter(outputStream);
//控制台输入内容
Scanner sc = new Scanner(System.in);
printWriter.write(sc.next());
printWriter.flush();
//第四步:关闭资源
printWriter.close();
outputStream.close();
bufferedReader.close();
reader.close();
inputStream.close();
socket.close();
server.close();
}
}
四、客户端
第一步:创建Socket客户端,指定服务器地址(此处是本机地址)和端口
第二步:与服务器端连接成功后,给服务器端发送信息
第三步:获取输入流得到服务器端的消息
第四步:关闭资源
package socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args)throws Exception {
//1.创建Socket客户端,指定服务器地址(此处是本机地址)和端口
Socket socket=new Socket("127.0.0.1",8888);
System.out.println("这是客户端*********");
//2.连接成功后发送信息
//获取输出流
OutputStream outputStream=socket.getOutputStream();
PrintWriter out=new PrintWriter(outputStream);
Scanner sc= new Scanner(System.in);
out.write(sc.next());
out.flush();
//这一句很重要,要写在前面,如果不写在这里,Socket Server 端的代码就会卡在读取inputStream中
socket.shutdownOutput();
//3.获取输入流得到服务器端的响应
InputStream inputStream= socket.getInputStream();
InputStreamReader isr=new InputStreamReader(inputStream);
BufferedReader br=new BufferedReader(isr);
String info=null;
while((info=br.readLine())!=null){//循环读取信息
System.out.println("收到服务端的响应:"+info);
}
br.close();
isr.close();
out.close();
out.close();
socket.close();
}
}
五、运行效果
启动server端和client端之后,在客户端输入hello,服务器端回复hi
六、总结
Socket通信的server端和Client端交互过程如图所示:
例子中只能实现服务器端和客户端互相发送一条消息,如果想相互交互多条消息,在客户端将 与服务端相互通信的语句加个while循环就好。