Socket 套接字概念
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电,有的提供110伏交流电,有的则提供有线电视节目。客户软件将插头插到不同编号的插座,就可以得到不同的服务。
利用Socket建立网络连接的步骤
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
1、服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
2、客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。 为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3、连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。
而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
简单了解了Socket的原理之后我们知道:
- 1、两个程序进行通信是通过端口号进行连接的
- 2、客户端要访问服务器是需要一个ip地址和一个端口的
- 3、每一次只能发送一条数据
为了更理解Socket套接字,必须要先理解Java中的IO流:
ServerSocket类
serverSocket在线API: http://www.apihome.cn/api/java/serverSocket.html
使用serverSocket创建服务端
- 构造函数:
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
- 方法:
Socket accept() 侦听并接受到此套接字的连接。
- 服务端代码实现
/**
* 服务器
*/
public static void test1() {
try {
System.out.println("服务已经启动,监听端口8888");
//创建一个服务端,监听端口8888
ServerSocket serverSocket = new ServerSocket(8888);
//等待客户端连接过来
Socket socket = serverSocket.accept();
System.out.println("来客IP:" + socket.getInetAddress());
//socket.getOutputStream() 获取客服端的输出流
OutputStream os = socket.getOutputStream();
//socket.getInputStream() 获取客服端的输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
//用来给客户端发送消息
PrintWriter pw = new PrintWriter(os);
pw.println("welcome to my home");
pw.flush();
String line = "";
while(true){
line = br.readLine();
System.out.println(line);
if (line.equals("exit")) {
System.out.println("拜拜");
break;
}
}
os.close();
pw.close();
br.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
这样就成功开启了一个服务端了
然后我们用telnet连接一下
telnet 192.168.20.168 8888
输入ctrl + ] 就可以发送信息了
我现在输入 一段话过去
服务端就接收到了信息
客服端向服务器发送 exit
服务器端接收就退出了
这样一个简单的服务器端就制作好了
实现对讲机功能
特点是一个说完,另一个才能说,一问一答
- 代码实现
/**
* 模拟对讲机效果。特点是一个说完,另一个才能说,一问一答
*/
public static void test4() {
//创建一个服务端
System.out.println("服务已经启动,监听端口8888");
try {
ServerSocket serverSocket = new ServerSocket(8888);
//接受一个客户端
Socket socket = serverSocket.accept();
//服务端的输入流,服务器端的scanner
Scanner serverKeybordScanner = new Scanner(System.in);
//服务端的输出流
PrintWriter serverPw = new PrintWriter(socket.getOutputStream());
//客户端的输入流
Scanner clientScanner = new Scanner(socket.getInputStream());
//如果客户端的输入流有下一行
while(clientScanner.hasNextLine()){
String msg = clientScanner.nextLine();
System.out.println("客服端的输入:" + msg);
if (msg.equals("exit")) {
break;
}
//每次客户端输入一段话过来,我们都要从键盘输入一段话,并且发送给客服端
if (serverKeybordScanner.hasNextLine()) {
//输入一段话
String serverMsg = serverKeybordScanner.nextLine();
//System.out.println("服务器输入:" + serverMsg);
//发送给客户端
serverPw.println("服务器 :" + serverMsg);
serverPw.flush();
}
}
serverPw.close();
serverKeybordScanner.close();
clientScanner.close();
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
使用telnet做为我们的客户端连接一下
这时客户端发送一个数据过去:hello I am Client
服务端获取到数据之后,回复一下
客户端接收到服务端的消息,并发送exit到服务器
服务器接收到exit之后程序就结束了
这样一个简单的对讲机功能就实现了
模拟一个获取内容的木马
客户端发送一个 文件的绝对路径 到服务端
服务端接收到文件路径之后将文件的内容返回给客户端
为了方便测试我在项目目录下创建了一个 test.txt 的文件
- 代码实现
/**
* 服务端
* 用来获取文件的绝对路径
* 客户端输入一个文件名,服务端就把指定的文件的内容返回给客户端
*/
public static void test5() {
System.out.println("服务器开启!");
//1、创建一个服务端,监听一个端口
ServerSocket serverSocket;
Socket socket = null;
try {
serverSocket = new ServerSocket(8888);
//2、获取一个客户端的对象
socket = serverSocket.accept();
//打印输出客服端的IP地址
System.out.println("来客IP:" + socket.getInetAddress());
//拿到用户的输入流
Scanner scanner = new Scanner(socket.getInputStream());
//拿到用户的输出流
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//如果捕获到有下一行
while(scanner.hasNextLine()){
//3、接受客服端的一个文件名(绝对路径)
String fileName = scanner.nextLine();
//4、读取文件
String content = getContentFromFile(fileName);
//5、把文件返回给客户端
pw.println(content);
//刷新
pw.flush();
}
scanner.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 从文件路径中找到文件并获取内容
* @param fileName
* @return content
*/
public static String getContentFromFile(String fileName) {
StringBuffer content = new StringBuffer();
Scanner scanner;
try {
scanner = new Scanner(new FileInputStream(fileName),"UTF-8");
while (scanner.hasNextLine()) {
content.append(scanner.nextLine()).append("\n");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return content.toString();
}
这样就开启了一个服务端
使用telnet连接一下,服务器捕获到了连接
然后输入test.txt 文件名 就获取到了内容
Socket类
刚刚介绍了一个服务端的创建过程,里面都是使用telnet作为我们的客户端进行交互,那我们能不能自己制作一个客户端呢?
一对一聊天功能的实现
Socket在线API: http://www.apihome.cn/api/java/Socket.html
- 构造方法:
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
- 方法:
InputStream getInputStream() 返回此套接字的输入流。
OutputStream getOutputStream() 返回此套接字的输出流。
创建Server项目
/**
* 服务器端
* 端口7777
* @author Lingdu
*
*/
public class Server {
/**
* 开启一个服务器
*/
public static void openServer() {
System.out.println("服务器已开启!");
try {
//服务器的端口
ServerSocket serverPort = new ServerSocket(7777);
//等待一个客服端的连接
Socket client = serverPort.accept();
//拿到用户的输入流
DataInputStream clientDis = new DataInputStream(client.getInputStream());
//拿到用户的输出流
DataOutputStream clientDos = new DataOutputStream(client.getOutputStream());
//服务器自己的输入流
BufferedReader myBf = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
while(true){
//读取来自客户端的信息
String clientMsg = clientDis.readUTF();
if (!clientMsg.equals("")) {
System.out.println("客服端说:" + clientMsg + "\n");
if (clientMsg.equals("exit")) {
break;
}
System.out.print("请输入:");
String serverSend = myBf.readLine();
clientDos.writeUTF((serverSend));
clientDos.flush();
}
}
client.close();
clientDis.close();
clientDos.close();
myBf.close();
serverPort.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
public static void main(String[] args) {
openServer();
}
}
创建Client项目
package com.lingdu.client;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 客户端
* @author LingDu
*
*/
public class Client {
public static void myClient(String host,int port) {
try {
//通过IP跟端口连接到服务器
Socket socket = new Socket(host, port);
//拿到服务器的输入流
DataInputStream serverDis = new DataInputStream(socket.getInputStream());
//拿到服务器的输出流
DataOutputStream serverDos = new DataOutputStream(socket.getOutputStream());
//客户端自己的输入流
BufferedReader myBf = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
while(true){
System.out.print("请输入:");
String clientSend = myBf.readLine();
serverDos.writeUTF((clientSend));
serverDos.flush();
//读取来自服务器的消息serverDis.readUTF();
String serverMsg = serverDis.readUTF();
System.out.println("服务器说:" + serverMsg + "\n");
if (serverMsg.equals("exit")) {
break;
}
}
socket.close();
serverDis.close();
serverDos.close();
myBf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
myClient("192.168.20.163",7777);
}
}
运行之后 服务端就接收到了连接
此时客户端进入输入状态,可以向服务器发送一条信息
服务器接收到客户端的信息
- 此时服务端进入输入状态,可以向客户端回复一条信息
客户端收到服务端的回复
- 这时客户端又会进入输入状态
这样一个简单的聊天软件就实现了。
总结
Socket是一种常连接,是 半双工1 的数据传输
Socket套接字在平常的的业务开发中并不会使用到,因为它会有很多缺陷,这个教程是为了帮助我们更好的去理解网络的概念。