Socket概述
Socket,套接字,就是两台主机之间逻辑连接的端点。Socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的协议端口。
Socket实际上是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
要通过互联网进行通信,至少需要一对套接字,其中一个运行于客户端,称之为 Client Socket,另一个运行于服务器端,称之为 Server Socket。
Socket工作流程:
1. 服务器监听。服务器监听,是指服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
2. 客户端请求。客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端接字提出连接请求。
3. 连接确认。连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,就会响应客户端套接字的请求,建立一个新的线程,并把服务器端套接字的描述发送给客户端。一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,接收其他客户端套接字的连接请求。
Socket实现TCP
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。这里就不具体展开描述了。
Socket是对TCP/IP协议的封装,要进行通信起码需要一个客户端和一个服务器端。
服务器端实现:
//服务器端
public class TcpServer {
public static void main(String[] args) {
try {
//创建一个serverSocket监听端口
ServerSocket server = new ServerSocket(9090);
System.out.println("启动服务器……");
//阻塞式监听
Socket socket = server.accept();
System.out.println("连接客户端……");
//读取客户端发送的数据
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String buffer = is.readLine();
System.out.println(buffer);
//向客户端发送数据
String rep = "Hello,Client!";
BufferedWriter os = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
os.write(rep);
//不加换行符readLine方法获取不到数据
os.write("\n");
os.flush();
//释放资源,关闭连接
os.close();
is.close();
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端实现:
public class TcpClient {
public static void main(String[] args) {
String message = "Hello,Server! I'm comming!";
try {
//创建socket与服务端建立连接
Socket socket = new Socket("127.0.0.1",9090);
//发送数据
BufferedWriter os = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
os.write(message);
os.write("\n")
os.flush();
//接受响应
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String rep = is.readLine();
System.out.println(rep);
//释放资源,关闭连接
is.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
先启动服务器端,再启动客户端就可以进行一次简单的TCP通信啦。
Socket实现UDP
UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
当然与TCP相比,UDP效率会更高,但是安全性就有所欠缺。只要有ip和端口就可以进行数据的交互了。
UDP通信也起码有一个发送方和一个接收方。
发送方实现:
public class UdpSender {
public static void main(String[] args) throws Exception{
//1.建立一个Socket
DatagramSocket datagramSocket = new DatagramSocket();
//2.建立一个包
//发送的消息
String message = "你好,服务器";
//发送给谁
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9000;
DatagramPacket datagramPacket = new DatagramPacket(message.getBytes(),message.getBytes().length, localhost,port);
//3.发送包
datagramSocket.send(datagramPacket);
//4.关闭流
datagramSocket.close();
}
}
接收方实现:
public class UdpServer {
public static void main(String[] args) throws Exception{
//1.开放端口
DatagramSocket datagramSocket = new DatagramSocket(9000);
//2.接收数据包
byte[] buffer = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buffer,0,buffer.length);//接收
datagramSocket.receive(datagramPacket);//阻塞接收
System.out.println(datagramPacket.getAddress().getHostAddress());// 127.0.0.1
//拆包,获取包中的数据
byte[] data = packet.getData();
String receiveData = new String(data,0,data.length);
System.out.println(receiveData);
//3.释放资源
datagramSocket.close();
}
}
Socket实现HTTP
HTTP:超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
HTTP是基于客户/服务器模式,且面向连接的。典型的HTTP事务处理有如下的过程:
(1)客户与服务器建立连接;
(2)客户向服务器提出请求;
(3)服务器接受请求,并根据请求返回相应的响应;
(4)客户与服务器关闭连接。
简单来说,HTTP是建立在TCP之上的一个应用层协议。进行HTTP通信就要进行TCP连接,并且其对交互的消息格式进行了规定,能传输各种各样的数据(文字,视频,图片等等)。
网页上有很多的HTTP协议的体现,随意打开控制台就能看见。
看看一个简单的HTTP通信的消息格式:
请求消息格式:
GET /api/usercenter/login?_=1637669107082 HTTP/1.1
Host: baike.baidu.com
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
sec-ch-ua-platform: "Windows"
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://baike.baidu.com/item/%E5%A5%97%E6%8E%A5%E5%AD%97/9637606?fromtitle=socket&fromid=281150&fr=aladdin
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: BIDUPSID=7C148573D75E9290853D186B81524028;
...
响应消息格式:
HTTP/1.1 200 OK
Connection: keep-alive
Content-Security-Policy-Report-Only: default-src https: 'unsafe-inline' 'unsafe-eval' data: blob: ; report-uri https://reports.baidu.com/csp-report/baike
Content-Type: application/json
Date: Tue, 23 Nov 2021 12:05:07 GMT
Server: Apache
Transfer-Encoding: chunked
...
HTTP的消息格式大致有以下几个部分:
请求消息:
请求行,请求头,请求体,请求空行
响应消息:
响应行,响应头,响应体,响应空行
既然HTTP协议是基于TCP协议的,自然也要有一个服务器端和一个客户端。
服务端的实现:
public class HttpServer {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
System.out.println("一个新的HTTP连接:" + socket.getLocalAddress() + ":" + socket.getPort());
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter os = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String requestHeader = "";
String message;
while ((message = is.readLine()) != null) {
message += "\r\n";
requestHeader += message;
//如果还有数据就继续读取
if ("\r\n".equals(message)) {
break;
}
}
System.out.println("Request Header:");
System.out.println(requestHeader);;
String responseBody = "Request Header:\n" + requestHeader;
String responseHeader = "HTTP/1.0 200 OK\r\n" +
"Content-Type: text/plain; charset=UTF-8\r\n" +
"Content-Length: "+ responseBody.getBytes().length + "\r\n" +
"\r\n";
System.out.println("Response Header:");
System.out.println(responseHeader);
os.write(responseHeader);
os.write(responseBody);
os.flush();
os.close();
is.close();
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端可以使用浏览器也可以使用curl命令:
1.使用curl
打开命令行窗口,输入curl -i http://127.0.0.1:8888
客户端:
服务器端:
2.使用浏览器
访问:http://localhost:8888
客户端:
服务端: