网络通信
网络
ip地址
域名与端口号
通信协议
TCP与UDP
InetAddress类
Socket
TCP网络通信编程

注:当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP 来分配的,是不确定且随机的。
编程举例
普通版


客户端
public class Client {public static void main(String[] args) throws IOException {Socket socket = new Socket(InetAddress.getLocalHost(),9999);//连接本机的9999端口,连接成功返回socket对象System.out.println("客户端socket返回: " + socket.getClass());OutputStream outputStream = socket.getOutputStream(); //通过socket.getOutputStream()获得输出流outputStream.write("hello,server".getBytes()); //写入数据(需要调用getBytes方法)outputStream.close(); //关闭流和socketsocket.close();}}
服务端
class Server {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9999); // 本机的9999端口开始监听(要求本机没有其它服务在监听9999)System.out.println("服务端,9999端口监听,等待连接..."); // 这个ServerSocket可以通过accept()方法 返回多个Socket,允许多个客户端连接服务器的并发//当没有客户端连接9999端口时,程序会阻塞,等待连接Socket socket = serverSocket.accept(); //如果有客户端连接,则会返回Socket对象,程序继续InputStream inputStream = socket.getInputStream(); //连接上后,通过 socket.getInputStream得到输入流对象byte[] buf = new byte[1024];int readLen = 0;while((readLen = inputStream.read(buf))!=-1){System.out.println(new String(buf,0,readLen));} //参见IO流,这里每次打印一个字符串inputStream.close(); //记得最后关闭流对象和socket,否则会占用资源socket.close();}}
进阶版

这次需要客户端做出回应,那么就需要考虑一个问题:什么时候服务端才算接收完了。就比如说两个聊天,A必须等B说完才能回应。我们需要加上对应的shutdownOutput() / shutdownInput()
总体的思路就是:让客户端先用OutputStream,服务端接收。然后用shutdown断开客户端的输出流。接着服务端用OutputStream,客户端接收。
客户端:
public class Client {public static void main(String[] args) throws IOException {//连接本机的9999端口,连接成功返回socket对象Socket socket = new Socket(InetAddress.getLocalHost(),9999);System.out.println("客户端socket返回: " + socket.getClass());//通过socket.getOutputStream()获得输出流OutputStream outputStream = socket.getOutputStream();outputStream.write("hello,server".getBytes());socket.shutdownOutput(); //停止输出流(告诉服务端我停止输入了)InputStream inputStream = socket.getInputStream();int readLen = 0;byte[] bytes = new byte[1024];while((readLen = inputStream.read(bytes))!=-1){System.out.println(new String(bytes,0,readLen));}inputStream.close();outputStream.close(); //关闭流和socketsocket.close();}
服务端:
class Server {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9999);System.out.println("服务端,9999端口监听,等待连接...");Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream(); //输入流int readLen = 0;byte[] bytes = new byte[1024];while((readLen = inputStream.read(bytes))!=-1){System.out.println(new String(bytes,0,readLen));} //读入数据三部曲OutputStream outputStream = socket.getOutputStream(); //开始反输出outputStream.write("hello,client".getBytes());socket.shutdownOutput(); //停止输出流outputStream.close(); //断开inputStream.close();socket.close();}}
把字节流改成字符流版本:
修改输出:
OutputStream outputStream = socket.getOutputStream();OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); //转换流BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); //用转换流为中介,把字节流转换成字符流bufferedWriter.write("hello,server");bufferedWriter.newLine(); //插入一个换行符,表示写入的内容结束,注意:要求对方使用 readLine方法接收bufferedWriter.flush(); //刷新
修改输入:
//用转换流作为中介创建 BufferedReaderBufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String s = bufferedReader.readLine(); //用readLine读入System.out.println(s);
收发文件案例


大致思路:把磁盘上图片输入到文件字节数组,然后通过Socket发送到服务端的Socket,重新解码为文件数据,然后放到某个目录上。服务端发送收到图片的消息,客户端显示收到的消息。
解释一下StreamUtils:这是一个自己定义的库,里面有各种各样的方法。我们要用到的是下面这几个:

首先是客户端:
1. 连接本机的8888端口。
//连接本机的8888端口,连接成功返回socket对象Socket socket = new Socket(InetAddress.getLocalHost(),8888);
2. 把要传输的文件先转化为byte数组,存储数据。
String filePath = "d:\\tupian1.png"; //要传输的文件路径BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));byte[] bytes = StreamUtils.streamToArray(bis); //把文件存到byte数组中
3. 客户端传输数据到服务器。
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); bos.write(bytes); //输出到服务器 socket.shutdownOutput();//暂停输出,让服务器回应
4. 接收服务器的回应。
InputStream inputStream = socket.getInputStream(); //接收服务器回应String s = StreamUtils.streamToString(inputStream); //转化成字符串System.out.println(s);
5. 关闭各种流与socket。
inputStream.close();bis.close();bos.close();socket.close();
然后是服务器:
1. 创建端口等待连接。
ServerSocket serverSocket = new ServerSocket(9999);System.out.println("服务端,8888端口监听,等待连接...");Socket socket = serverSocket.accept(); //返回端口对应的socket,等待连接
2. 接收客户端发来的数据,保存在byte数组里。
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());//因为图片是二进制文件,因此用InputStreambyte[] bytes = StreamUtils.streamToByteArray(bis);//接收客户端数据,bytes数组存储的就是图片的内容
3. 把byte数组里的数据还原到指定文件中。
String desFilePath = "src:\\tupian2.png"; //存放文件的位置BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(desFilePath));//输出到指定文件中,用输出流bos.write(bytes);//输出,此时文件已复制完成
4. 告诉客户端自己已经收到数据。
//接下来就是告诉客户端自己收到信息了(用字符流输出汉字信息)BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//用OutputStreamWriter修改为字符流输出writer.write("收到图片");writer.flush(); //注意刷新socket.shutdownOutput(); //暂停输出
5. 关闭各种流与socket。
bos.close();bis.close();socket.close();serverSocket.close();
netstat指令
UDP网络编程
基本介绍
基本流程
应用案例

接收端A:
public class Receiver {public static void main(String[] args) throws IOException {DatagramSocket socket = new DatagramSocket(9999); // 1.创建一个 DatagramSocket对象,准备在9999接收数据byte[] buf = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buf,buf.length); // 2.构建一个 DatagramPacket 对象,准备存放数据 (一个数据包最大64k)socket.receive(packet);//3.调用接收方法,将传输的数据填充到 packet中//4. 可以把packet进行拆包,取出数据并显示。int length = packet.getLength(); //实际接收到的数据字节长度byte[] data = packet.getData(); // 接收到的数据(以字节数组形式保存)String s = new String(data,0,length);System.out.println(s);//5.关闭资源socket.close();}}
接收端B:
public class Sender {public static void main(String[] args) throws IOException {//1.创建 DatagramSocket对象,准备在9998端口接收数据DatagramSocket socket = new DatagramSocket(9998);//注意:在UDP中,即使是发送端也是可以接收数据的。//2.将需要发送的数据,封装到 DatagramPacket对象byte[] data = "hello,明天吃火锅".getBytes();//3. 有内容的DatagramPacket构造器:内容字节数组 数组长度 主机IP 端口DatagramPacket packet =new DatagramPacket(data,data.length, InetAddress.getByName("192.168.43.8"),9999);socket.send(packet);//4.发送内容socket.close(); //关闭资源}}
想要反过来发信息,只需要角色对换重新写一遍send和receive即可。注意 receive和 send方法都是socket的,即使是在9998端口监听的socket,也可以向9999端口发送数据。













