TCP编程:socket概念及使用方法(基础教程)
Socket (套接字)是网络编程中的核心概念,用于实现不同设备或进程之间的通信,是 TCP/IP 协议族中应用层与传输层之间的编程接口。
主要作用:
1. 建立连接:在 TCP 通信中,客户端和服务端通过 Socket 建立双向的连接通道,使得数据可以在两者之间传输。
2.数据传输:支持字节流( InputStream / OutputStream )的读写操作,能实现可靠的数据发送与接收。
3.标识通信端点:每个 Socket 都有对应的 IP 地址和端口号,用于唯一标识网络中的通信双方。
socket建立数据流示意图:
上机实操:
实验目的:创建服务端和客户端,客户端给服务端发送一条“Hello,sever”,服务端收到后给客户端返回一条消息“Hello,client”程序终止。
前提条件:创建两个不同的类文件,都写入main函数。一个作为服务端,另一个作为客户端。
1.首先在服务端建立监听,监听本机IP的一个端口号(我使用6666作为端口号)的端口。注意:端口号不建议使用0~1023间的端口,这些端口被IANA(互联网号码分配权威机构)预留,分配给了互联网上广泛使用的“知名服务”。
//要求本机没有其他服务在监听6666
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务端等待链接...");
2.当有客户端访问对应端口时,通过ServerSocket对象的accept方法创建socket对象。当无客户端运行9999端口时,程序会阻塞等待链接。
Socket socket = serverSocket.accept();
3.客户端先找到要访问的IP地址(我这里是访问本机的IP地址,所以可以用InetAddress.getLocalHost()方法获取本机IP地址)和需要访问的端口号来访问对应的主机和端口,如果访问成功的话,会在客户端成功创建socket对象。
Socket socket = new Socket(InetAddress.getLocalHost(),6666);
//如果访问成功,就创建socket对象
System.out.println("客户端进入");
4.在客户端的socket中通过getOutputStream()方法创建一个OutputStream流在输出流中写入需要传输的内容。
OutputStream outputStream1 = socket.getOutputStream();
output.write("Hello,serve!".getBytes());
每次使用完OutputStream流后,都需要对socket对象使用shutdownOutput()方法标注输出完毕。就好比我对你说话,我说了一句“明天出去玩吗?”你对我说“哇”。此时我不知道你是说完了还是没说完,所以我就一直等待你下达对话完毕的命令。shutdownOutput()就是一种标注输出完毕的方法。
5.使用shutdownOutput()方法标注输出完毕,此时我就已经把一句完整的话说出去了。
socket.shutdownOutput();
6.此时服务端监听到对应端口被客户端访问到了,此时服务端会创建一个在服务端的socket对象。我们用getInputStream()方法从socket对象中获取一个InputStream流并输出到控制台。如此,就完成了从服务客户端到服务端的一次通讯。
//创建流通道
InputStream inputStream0 = socket.getInputStream();
byte[] b = new byte[1024];
int readLen = 0;
while((readLen = inputStream0.read(b))!=-1) {System.out.print(new String(b, 0, readLen));
}
同理,从服务端到客户端的通讯方式也是如此:
1.服务端通过getOutputStream()方法创建OutputStream对象将需要传输的内容放入socket对象里。
OutputStream outputStream0 = socket.getOutputStream();
String fileContents = "hello,client";
outputStream0.write(fileContents.getBytes());
//说完了,标注说完了
socket.shutdownInput();
2.客户端通过InputStream流接收socket中的信息并从控制台输出。
InputStream inputStream1 = socket.getInputStream();
byte[] b = new byte[1024];
int readLen = 0;
while((readLen = inputStream1.read(b))!=-1) {System.out.print(new String(b, 0, readLen));
}
最后,一定要关闭客户端和服务端的socket流和InputStream/OutputStream释放资源,防止端口被占用。
//服务端关闭流
outputStream0.close();
inputStream0.close();
socket.close();
//客户端关闭流
inputStream1.close();
outputStream1.close();
socket.close();
如果在程序运行过程中出现端口被占用的情况(仅限于实验用到的端口号如6666),可以在控制台输入"netstat -ano"调出找到端口号对应的PID,在任务管理器的详细信息中将对应PID的进程结束即可。