Socket(套接字)开发网络应用程序
两端都要有Socket,网络通信实质是指Socket间的通信
Socket允许把网络连接当作一个流,数据在两个Socket间以IO传输
分为客户端和服务端
读写数据时需要用到两个方法:
socket.getOutputStream()
socket.getInputStream()
Socket分为两类:TCP网络编程,UDP网络编程
TCP网络编程
netstat 指令
netstat -an:查看当前主机的网络情况,包括端口监听和网络连接情况
netstat -an | more 分页显示
netstat -anb | more 分页显示那个程序在监听对应端口(管理员状态)
TCP网络编程案例
案例1:客户端发送消息,服务端接收来自客户端的消息,并且打印出收到的消息。
//服务端
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//1,服务器端开始监听9999端口,如果本机有其他服务监听次端口,那么会抛出异常
ServerSocket serverSocket=new ServerSocket(9999);
System.out.println("服务端正在监听9999端口。。。");
//2,当没有客户端连接本机的9999端口时,程序会阻塞
//如果有客户端连接,则会返回Socket对象,程序继续
//serverSocket可以寄接收来自多个客户端的消息
Socket socket=serverSocket.accept();
System.out.println("服务端socket="+socket.getClass());
InputStream inputStream = socket.getInputStream();
//4,IO读取
byte[] buf=new byte[1024];
int len=0;
while ((len=inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,len));
System.out.println("服务端接收数据成功。。。");
}
//5,关闭资源
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务端关闭成功。。。");
}
}
//客户端
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端socket返回"+socket.getClass());
//2得到和socket关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3通过输出流,项数据通道中输入数据
outputStream.write("hello server".getBytes());
//关闭输出流和socket,解决资源
outputStream.close();
socket.close();
System.out.println("客户端已经退出。。。。");
}
}
先启动服务端,当没有客户端来连接时,服务端会陷入阻塞状态
我们再启动客户端。
此时我们已经将客户端要发送的数据发送到服务端,我们再看看服务端接收到了什么数据。
这个最简单的案例这里就结束了,接下来我们改进一下程序,使得服务端接收到客户端发送的数据后,回复一条表示已经收到信息的话。
案例二:
//服务端
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//1,服务器端开始监听9999端口,如果本机有其他服务监听次端口,那么会抛出异常
ServerSocket serverSocket=new ServerSocket(9999);
System.out.println("服务端正在监听9999端口。。。");
//2,当没有客户端连接本机的9999端口时,程序会阻塞
//如果有客户端连接,则会返回Socket对象,程序继续
//serversocket可以寄接收来自多个客户端的消息
Socket socket=serverSocket.accept();
System.out.println("服务端socket="+socket.getClass());
InputStream inputStream = socket.getInputStream();
//4,IO读取
byte[] buf=new byte[1024];
int len=0;
while ((len=inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
System.out.println("服务端接收数据成功。。。");
//5.服务端接收到客户端的信息后,向客户端发送一条信息。
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello client".getBytes());
socket.shutdownOutput();
//6,关闭资源
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务端关闭成功。。。");
}
}
//客户端
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端socket返回"+socket.getClass());
//2得到和socket关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3通过输出流,项数据通道中输入数据
outputStream.write("hello server".getBytes());
//写入结束标志,否则服务端会阻塞在while读取的过程中。
socket.shutdownOutput();
//4.获取与socket关联的输入流,读取服务端发送过来的数据
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
while ((len=inputStream.read(bytes) )!= -1){
System.out.println(new String(bytes,0,len));
}
//5,关闭输出流和socket,解决资源
outputStream.close();
socket.close();
System.out.println("客户端已经退出。。。。");
}
}
注意:当客户端向输出流中写入数据后,必须再后面添加一个socket.shutdownOutput();它的作用是告诉服务端的输出流中不会再写入任何数据了,不用再读取了。
如果不添加这条语句的话,那么服务端会再while循环中不断的读取,却永远读不到-1结束标志,服务端也就陷入了阻塞。
同时客户端继续向下执行,因为接下来它要接收服务端回复的消息,但因为服务端已经陷入了阻塞,因此客户端中的while循环也不能脱离,也陷入了阻塞。
因此此时服务端和客户端都陷入阻塞,都等待着对方的消息。
这时,可能又有一个新的疑问产生了:那为什么案例1中客户端发送完数据后,也没有写socket.shutdownOutput();为什么它就没有陷入阻塞呢?
这时因为客户端1接下来的语句中没有while循环,直接关闭了流还有socket,它起的作用和socket.shutdownOutput() 类似于告诉对面端:我想写的东西已经写完了,你不要再读了!
UDP
UDP没有明确的服务端和客户端,只有发送端和接收端
发送数据和接收数据据都是通过DatagramSocket来完成的
将数据封装成DatagramPacket 对象发送( 装包)
接收数据需要拆包
DatagramSocke可以指定在哪一个端口接收
基本流程
1,两个核心类DatagramPacket和DatagramSocket
2,建立发送端和接收端
3,发送数据前建立数据包DatagramPacket
4,调用DatagramSocket的发送接收方法
5,关闭DatagramSocket
案例1
public class UDPSender {
public static void main(String[] args) throws IOException {
//创建一个DatagramSocket对象,准备在9998端口接收数据
DatagramSocket datagramSocket = new DatagramSocket(9998);
//2,将需要发送的数据发送到DatagramSocket对象中
String data = "Hello UDP,明天吃火锅";
//3,封装的对象包括字节数组,数组长度,端口等信息
DatagramPacket datagramPacket = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getByName("192.168.74.1"), 9999);
//4,发送数据
datagramSocket.send(datagramPacket);
//=====接收A端回复的消息===
//2,创建DatagramPacket接收数据,一个DatagramPacket最大存64K数据,不适合传大量数据
datagramPacket=new DatagramPacket(new byte[1024],1024);
//3,调用接收方法,如果没有数据,会阻塞
System.out.println("等待接收数据。。。。");
datagramSocket.receive(datagramPacket);
//4,对数据包进行拆包,取出数据并显示
int length=datagramPacket.getLength();//实际收到的数据字节长度
byte[] data1=datagramPacket.getData();//实际接收到的数据
String msg=new String(data1,0,length);
System.out.println("接收到的数据是:"+msg);
//关闭资源
datagramSocket.close();
System.out.println("B端退出");
}
}
//接收数据
public class UDPReceiver {
public static void main(String[] args) throws IOException {
//1,创建一个DatagramSocket对象,准备在9999端口接收数据据
DatagramSocket datagramSocket=new DatagramSocket(9999);
//2,创建DatagramPacket接收数据,一个DatagramPacket最大存64K数据,不适合传大量数据
DatagramPacket datagramPacket=new DatagramPacket(new byte[1024],1024);
//3,调用接收方法,如果没有数据,会阻塞
System.out.println("等待接收数据。。。。");
datagramSocket.receive(datagramPacket);
//4,对数据包进行拆包,取出数据并显示
int length=datagramPacket.getLength();//实际收到的数据字节长度
byte[] data=datagramPacket.getData();//实际接收到的数据
String msg=new String(data,0,length);
System.out.println("接收到的数据是:"+msg);
//====回复B端消息
//2,将需要发送的数据发送到DatagramSocket对象中
String data1 = "Hello UDP,我收到了B端传递过来的消息";
//3,封装的对象包括字节数组,数组长度,端口等信息
datagramPacket = new DatagramPacket(data1.getBytes(), data1.getBytes().length, InetAddress.getByName("192.168.74.1"), 9998);
//4,发送数据
datagramSocket.send(datagramPacket);
//释放资源
datagramSocket.close();
System.out.println("A端结束。。");
}
}