计算机网络之socket,什么是socket
两个进程之间如果需要通信,最基本的前提,是能够唯一地标识一个进程,在本地进程通信中,我们可以使用PID,来唯一标识一个进程,
但PID只在本地唯一网络中的两个进程PID冲突的几率还是有的。
IP层的IP地址,可以唯一标识一台主机,而TCP协议和端口号可以唯一标识主机的一个进程。
这样我们可以利用IP地址加协议加端口号来唯一标识网络中的一个进程,就可以使用Socket进行通信了。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
什么是Socket
Socket是对TCP/IP协议的抽象,是操作系统对外开放的接口。
Socket适应其他的网络协议,使得程序员更方便地使用TCP/IP协议栈。
对TCP/IP协议的抽象,从而形成了我们知道的最基本的函数接口。
比如create,listen,connect,accept,send,read,write,等等。
Socket起源于UNIX,
Socket基于从打开到读和写,再到关闭的这种模式去实现的。
服务器和服务端各自维护一个文件,在建立连接打开后,可以向自己的文件写入内容,供对方读取,或者读取对方的内容,在通讯结束时,
就会关闭文件。
Socket通信流程(使用TCP协议通信的Socket为例讲解)
服务器首先创建通信ServerSocket,为ServerSocket绑定IP地址和端口号。
紧接着服务器创建Socket监听端口号的请求,随时准备接受客户端发来的连接。
服务器的Socket只是listen,没有打开 。
此时假设客户端创建了Socket,打开了Socket,并根据服务器的IP地址和端口号尝试去连接服务器的Socket,服务器的Socket接收到客户端Socket
请求,被动地打开,开始接收客户端的请求,直到客户端返回连接信息,这时候服务器的Socket进入到阻塞状态,即accept方法需要等待客户端返回连接信息后,才返回。同时开始接收下一个客户端的连接请求。
客户端在连接成功之后呢,向服务器发送连接状态信息,服务端在接收到客户端的连接信息之后,将accpet方法返回,并提示连接成功。
客户端向Socket去写入信息,服务器能收到并读取相关的信息,最 后在发送完数据后,客户端会关闭socket,服务端也会关闭Socket
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Socket相关的面试题
TCPServer
package com.interview.javabasic.socket;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws Exception{
//创建通信ServerSocket,并将ServerSocket绑定到6500端口
ServerSocket ss = new ServerSocket(65000);
//死循环,使得socket一直等待并处理客户端发送过来的请求;
while (true){
//监听6500端口,直到客户端返回连接信息后才返回
Socket socket = ss.accept();
//获取客户端的请求信息后,执行相关业务逻辑
new LengthCalculator(socket).start();
}
}
}
TCPClient
package com.interview.javabasic.socket;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws Exception{
//创建socket,并指定连接的是本机的端口号为65000的服务器的socket
Socket socket = new Socket("127.0.0.1",65000);
//获取输出流
OutputStream os = socket.getOutputStream();
//获取输入流
InputStream is = socket.getInputStream();
//将要传递给Server的字符串参数转换成byte数组,并把数组写入到输出流中
os.write(new String("hello world").getBytes());
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入的内容,存成byte数组,ch主要用来获取读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换成字符串,这里是从服务端回发回来的字符串参数的长度
String content = new String(buff,0,ch);
System.out.println(content);
//不要忘记关闭输入输出流及socket
is.close();
os.close();
socket.close();
}
}
LengthCalculator
package com.interview.javabasic.socket;
import jdk.internal.util.xml.impl.Input;
import javax.imageio.IIOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class LengthCalculator extends Thread{
//以socket为成员变量
private Socket socket;
//构造函数,接收服务器端socket实例,并且将该实例传递给本身的成员变量
public LengthCalculator(Socket socket){
this.socket = socket;
}
@Override
public void run(){
try{
//获取socket的输出流,
OutputStream os = socket.getOutputStream();
//获取socket的输入流
InputStream is = socket.getInputStream();
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入的内容,存成byte数组(read函数参数为数组),ch主要用来获取读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换成字符串,这里获取的内容是客户端发送过来的字符串参数
String content = new String(buff,0,ch);
System.out.println(content);
//往输出流里写入获得的字符串长度,回发给客户端
//write函数参数又是数组了,所以得从字符串转换成数组
os.write(String.valueOf(content.length()).getBytes());
//不要忘记关闭输入输出流以及socket
is.close();
os.close();
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
UDPServer
package com.interview.javabasic.socket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args) throws Exception{
//服务端接收客户端发送的数据报
DatagramSocket socket = new DatagramSocket(65001);//监听的窗口号
byte[] buff = new byte[100];//存储从客户端接收到的内容
DatagramPacket packet = new DatagramPacket(buff,buff.length);
//packet接收客户端发过来的内容,并将内容封装进DatagramPacket对象中
socket.receive(packet);
byte[] data = packet.getData(); //从DatagramPacket对象 中获取到真正存储的数据
//将数据从二进制转换成字符串形式
String content = new String(data,0,packet.getLength());
System.out.println(content) ;
//将要发送给客户端的数据转换成二进制
byte[] sendedContent = String.valueOf(content.length()).getBytes();
//服务端给客户端发送数据报
//从DatagramPacket对象中获取到数据的来源地址与端口号
DatagramPacket packetToClient = new DatagramPacket (sendedContent,
sendedContent.length,packet.getAddress(), packet.getPort());
socket.send(packetToClient); //发送数据给客户端
}
}
UDPClient
package com.interview.javabasic.socket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPClient {
public static void main(String[] args) throws Exception{
//客户端发数据报给服务端
DatagramSocket socket = new DatagramSocket();
//要发送给服务端的数据
byte[] buf = "Hello World".getBytes();
//将IP地址封装成InetAddress对象
InetAddress address = InetAddress.getByName("127.0.0.1");
//将要发送给服务端的数据封装成DatagramPacket对象 需要填写上ip地址与端口号
DatagramPacket packet = new DatagramPacket(buf,buf.length,address,
65001);
//发送数据给服务端
socket.send(packet);
//客户端接收服务端发送过来的数据报
byte[] data = new byte[100];
//创建DatagramPacket对象用来存储服务端发送过来的数据
DatagramPacket receivePacket = new DatagramPacket(data,data.length);
//将接收到的数据存储到DatagramPacket对象中
socket.receive(receivePacket);
//将服务端发送过来的数据取出来并打印到控制台
String content = new String(receivePacket.getData(),0,
receivePacket.getLength());
System.out.println(content);
}
}
欢迎大家关注我的微信公众号,获取你不知道的宝藏。