前言:不在本文中详细说明网络通信(具体的准备有时间开一个网络专栏,具体按照Cisco Packet进行实验模拟说明。)
本文主要记录java网络编程的具体实现代码(比较基础的一些封装实现。)
我们的计算机网络实现了将不同地理位置上的多台计算机以及相关的外部设备进行连接起来,在网络的操作系统,网络管理软件以及网络通信协议的管理和协调下,实现资源共享以及进行幸喜或者数据传递。
计算机网络编程就是在我们具体的网络通信协议的规则下,进行实现不同计算机上运行程序的一个数据交换或者说是数据通信的一个过程实现。
上面这个三要素也就是我们java网络编程主要围绕的基本要素。
windows可以通过终端命令ipconfig查看本机的ip地址。linux对应的是ifconfig,也可以是ip addr进行查看。
查看网络是否通的话可以用ping命令 特殊的地址是127.0.0.1是本机的回送地址,自己可以进行ping测试。
首先我们看这个类
public class InetAddressextends Objectimplements Serializable
该类下面还有两个子类,分别是Inet4Address和Inet6Address。我们目前就先用这个InetAddress类。
提供的方法
boolean equals(Object obj) 将此对象与指定对象比较。 byte[] getAddress() 返回此 InetAddress 对象的原始 IP 地址。 static InetAddress[] getAllByName(String host) 在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。 static InetAddress getByAddress(byte[] addr) 在给定原始 IP 地址的情况下,返回 InetAddress 对象。 static InetAddress getByAddress(String host, byte[] addr) 根据提供的主机名和 IP 地址创建 InetAddress。 static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。 String getCanonicalHostName() 获取此 IP 地址的完全限定域名。 String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。 String getHostName() 获取此 IP 地址的主机名。 static InetAddress getLocalHost() 返回本地主机。 int hashCode() 返回此 IP 地址的哈希码。 boolean isAnyLocalAddress() 检查 InetAddress 是否是通配符地址的实用例行程序。 boolean isLinkLocalAddress() 检查 InetAddress 是否是链接本地地址的实用例行程序。 boolean isLoopbackAddress() 检查 InetAddress 是否是回送地址的实用例行程序。 boolean isMCGlobal() 检查多播地址是否具有全局域的实用例行程序。 boolean isMCLinkLocal() 检查多播地址是否具有链接范围的实用例行程序。 boolean isMCNodeLocal() 检查多播地址是否具有节点范围的实用例行程序。 boolean isMCOrgLocal() 检查多播地址是否具有组织范围的实用例行程序。 boolean isMCSiteLocal() 检查多播地址是否具有站点范围的实用例行程序。 boolean isMulticastAddress() 检查 InetAddress 是否是 IP 多播地址的实用例行程序。 boolean isReachable(int timeout) 测试是否可以达到该地址。 boolean isReachable(NetworkInterface netif, int ttl, int timeout) 测试是否可以达到该地址。 boolean isSiteLocalAddress() 检查 InetAddress 是否是站点本地地址的实用例行程序。 String toString() 将此 IP 地址转换为 String。
比如我们可以这样取获取主机地址或者主机名
package internet;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Internet_1 {
public static void main(String[] args) throws UnknownHostException {
//确定主机地址的ip地址,主机名称可以是机器名称,也可以是ip地址
InetAddress address = InetAddress.getByName("兰舟千帆一号");
InetAddress localHost = InetAddress.getLocalHost();
byte[] address1 = localHost.getAddress();
String hostName1 = localHost.getHostName();
System.out.println(localHost);
String hostAddress = address.getHostAddress();
//System.out.println(hostAddress);
String hostName = address.getHostName();
System.out.println("主机名:"+hostName+"主机地址"+hostAddress);
}
}
DatagramSocket代表数据包套接字,当然这个类也有许多的说明和详细的方法使用。
public class DatagramSocketextends Object
此类表示用来发送和接收数据报包的套接字。
数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
在 DatagramSocket 上总是启用 UDP 广播发送。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加具体的地址时广播包也可以被接收
这是我们需要主要去了解的信息。我们首先要了解的是这个类具有发送数据和接收数据的功能。可以绑定到主机的端口。
主要的构造方法
DatagramSocket() 构建一个数据报套接字 ,绑定到本地主机的任何可用的端口 DatagramSocket(int port) 构建一个数据报套接字,绑定到本地主机的指定端口 DatagramSocket(int port, InetAddress laddr) 创建一个数据报套接字,绑定到指定的本地地址 DatagramSocket(SocketAddress bindaddr) 创建一个数据报套接字,绑定到指定的本地套接字地址 protected DatagramSocket(DatagramSocketImpl impl) 创建一个绑定的数据报套接字, 与指定的datagramsocketimpl相关(一般不使用)
提供的api方法
InetAddress getInetAddress() 返回此套接字连接的地址 InetAddress getLocalAddress() 获取绑定的套接字的本地地址 int getLocalPort() 返回此套接字绑定的本地主机上的端口号 SocketAddress getLocalSocketAddress() 返回此套接字绑定到的端点的地址 int getPort() 返回此套接字连接的端口号 void close() 关闭该数据报套接字 void connect(InetAddress address, int port) 将套接字连接到这个套接字的远程地址 void connect(SocketAddress addr) 将此套接字连接到远程套接字地址(IP地址+端口号) void bind(SocketAddress addr) 结合这个DatagramSocket到特定的地址和端口 void disconnect() 断开插座 boolean getBroadcast() 如果so_broadcast启用 DatagramChannel getChannel() 返回与此数据报套接字相关的独特的 DatagramChannel对象,如果任何 int getReceiveBufferSize() 得到这个 DatagramSocket的so_rcvbuf期权价值,即通过平台用于该 DatagramSocket输入缓冲区的大小。 SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点的地址,或如果它是无关的 null。 boolean getReuseAddress() 如果so_reuseaddr启用。 int getSendBufferSize() 得到这个 DatagramSocket的so_sndbuf期权价值,即缓冲区的大小由平台用于输出在这 DatagramSocket。 int getSoTimeout() 检索设置so_timeout。 int getTrafficClass() 获取交通类或类型的服务在IP数据报头的DatagramSocket发送的数据包。 boolean isBound() 返回套接字的绑定状态。 boolean isClosed() 返回套接字是否关闭或不关闭的。 boolean isConnected() 返回套接字的连接状态。 void receive(DatagramPacket p) 接收数据报包从这个插座。 void send(DatagramPacket p) 从这个套接字发送数据报包。 void setBroadcast(boolean on) 启用/禁用so_broadcast。 static void setDatagramSocketImplFactory(DatagramSocketImplFactory fac) 集的数据报套接字实现工厂的应用。 void setReceiveBufferSize(int size) 集so_rcvbuf选项,这 DatagramSocket指定值。 void setReuseAddress(boolean on) 启用/禁用so_reuseaddr套接字选项。 void setSendBufferSize(int size) 集so_sndbuf选项,这 DatagramSocket指定值。 void setSoTimeout(int timeout) 启用/禁用so_timeout以指定的超时时间,以毫秒为单位。 void setTrafficClass(int tc) 为从此 DatagramSocket 上发送的数据报在 IP 数据报头中设置流量类别 (traffic class) 或服务类型八位组 (type-of-service octet)。
public final class DatagramPacketextends Object
数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
主要的构造方法
DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。 DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 DatagramPacket(byte[] buf, int offset, int length) 构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。 DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。 DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。 DatagramPacket(byte[] buf, int length, SocketAddress address) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
提供的api方法
InetAddress getAddress() 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。 byte[] getData() 返回数据缓冲区。 int getLength() 返回将要发送或接收到的数据的长度。 int getOffset() 返回将要发送或接收到的数据的偏移量。 int getPort() 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。 SocketAddress getSocketAddress() 获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。 void setAddress(InetAddress iaddr) 设置要将此数据报发往的那台机器的 IP 地址。 void setData(byte[] buf) 为此包设置数据缓冲区。 void setData(byte[] buf, int offset, int length) 为此包设置数据缓冲区。 void setLength(int length) 为此包设置长度。 void setPort(int iport) 设置要将此数据报发往的远程主机上的端口号。 void setSocketAddress(SocketAddress address) 设置要将此数据报发往的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
上面引用了javaapi里面的说明。就部分api说明并构建功能。
package internet;
import java.io.IOException;
import java.net.*;
public class Udp_1 {
public static void main(String[] args) throws IOException {
//发送数据
//创建发送端Socket对象
DatagramSocket ds = new DatagramSocket();
//创建数据,将数据打包
byte[] bys = "Hello,udp".getBytes();//将数据转换为字节
int length = bys.length;//获取到数据长度
InetAddress address = InetAddress.getByName("兰舟千帆一号");
// System.out.println(address);
int port = 10086;//端口
//相当于数据包
DatagramPacket dp = new DatagramPacket(bys,length,address,port);
ds.send(dp);//发送数据包
ds.close();//关闭发送端
//接收数据
//创建接收端Sockec对象(需要指定一个端口)
}
}
package internet;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class udp_2 {
public static void main(String[] args) throws IOException {
//接收数据
//创建接收端Sockec对象(需要指定一个端口)
//接收端端口
DatagramSocket ds1 = new DatagramSocket(10086);
//创建数据包,用于接收数据
byte[] bys1 = new byte[1024];
DatagramPacket dp_1 = new DatagramPacket(bys1, bys1.length);
int len = dp_1.getLength();
ds1.receive(dp_1);
//解析数据包,并在控制台显示
byte[] data = dp_1.getData();
String data_s = new String(data,0,len);
System.out.println(data_s);
ds1.close();
}
}
这样的话,简单的首先可以定义两个类 发送端
package internet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class Udp_pratice {
public static void main(String[] args) throws IOException {
//创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket();
//键盘录入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line = br.readLine())!=null)
{
if("886".equals(line))
{
break;
}
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("兰舟千帆一号"),10086);
ds.send(dp);
}
}
}
接收端
package internet;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class udp_2 {
public static void main(String[] args) throws IOException {
//接收数据
//创建接收端Sockec对象(需要指定一个端口)
//接收端端口
DatagramSocket ds1 = new DatagramSocket(10086);
//创建数据包,用于接收数据
byte[] bys1 = new byte[1024];
DatagramPacket dp_1 = new DatagramPacket(bys1, bys1.length);
int len = dp_1.getLength();
while(true)
{
ds1.receive(dp_1);
//解析数据包,并在控制台显示
byte[] data = dp_1.getData();
String data_s = new String(data,0,len);
System.out.println(data_s);
}
// ds1.close();
}
}
这个程序怎么运行呢。其实你可以开启多个发送端,然后进行一个接收端。idea默认是不允许这样开启的,但是这样改一下就好啦!
把下面这个前面的对勾去掉。我用的idea版本比较老,可能不一样的版本设置不太一样。
这样运行就有两个发送端,一个接收端。
你这样开启两个终端,分别写数据,然后接收端都可以收到。
加粗样式
public class Socketextends Object
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。 套接字的实际工作由 SocketImpl 类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字。
提供的构造方法
Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字 Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 Socket(InetAddress host, int port, boolean stream) 已过时。 Use DatagramSocket instead for UDP transport. Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 Socket(Proxy proxy) 创建一个未连接的套接字并指定代理类型(如果有),该代理不管其他设置如何都应被使用。 protected Socket(SocketImpl impl) 使用用户指定的 SocketImpl 创建一个未连接 Socket。 Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。 Socket(String host, int port, boolean stream) 已过时。 使用 DatagramSocket 取代 UDP 传输。 Socket(String host, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程主机上的指定远程端口
提供的api接口
void bind(SocketAddress bindpoint) 将套接字绑定到本地地址。 void close() 关闭此套接字。 void connect(SocketAddress endpoint) 将此套接字连接到服务器。 void connect(SocketAddress endpoint, int timeout) 将此套接字连接到服务器,并指定一个超时值。 SocketChannel getChannel() 返回与此数据报套接字关联的唯一 SocketChannel 对象(如果有)。 InetAddress getInetAddress() 返回套接字连接的地址。 InputStream getInputStream() 返回此套接字的输入流。 boolean getKeepAlive() 测试是否启用 SO_KEEPALIVE。 InetAddress getLocalAddress() 获取套接字绑定的本地地址。 int getLocalPort() 返回此套接字绑定到的本地端口。 SocketAddress getLocalSocketAddress() 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。 boolean getOOBInline() 测试是否启用 OOBINLINE。 OutputStream getOutputStream() 返回此套接字的输出流。 int getPort() 返回此套接字连接到的远程端口。 int getReceiveBufferSize() 获取此 Socket 的 SO_RCVBUF 选项的值,该值是平台在 Socket 上输入时使用的缓冲区大小。 SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点的地址,如果未连接则返回 null。 boolean getReuseAddress() 测试是否启用 SO_REUSEADDR。 int getSendBufferSize() 获取此 Socket 的 SO_SNDBUF 选项的值,该值是平台在 Socket 上输出时使用的缓冲区大小。 int getSoLinger() 返回 SO_LINGER 的设置。 int getSoTimeout() 返回 SO_TIMEOUT 的设置。 boolean getTcpNoDelay() 测试是否启用 TCP_NODELAY。 int getTrafficClass() 为从此 Socket 上发送的包获取 IP 头中的流量类别或服务类型。 boolean isBound() 返回套接字的绑定状态。 boolean isClosed() 返回套接字的关闭状态。 boolean isConnected() 返回套接字的连接状态。 boolean isInputShutdown() 返回是否关闭套接字连接的半读状态 (read-half)。 boolean isOutputShutdown() 返回是否关闭套接字连接的半写状态 (write-half)。 void sendUrgentData(int data) 在套接字上发送一个紧急数据字节。 void setKeepAlive(boolean on) 启用/禁用 SO_KEEPALIVE。 void setOOBInline(boolean on) 启用/禁用 OOBINLINE(TCP 紧急数据的接收者) 默认情况下,此选项是禁用的,即在套接字上接收的 TCP 紧急数据被静默丢弃。 void setPerformancePreferences(int connectionTime, int latency, int bandwidth) 设置此套接字的性能偏好。 void setReceiveBufferSize(int size) 将此 Socket 的 SO_RCVBUF 选项设置为指定的值。 void setReuseAddress(boolean on) 启用/禁用 SO_REUSEADDR 套接字选项。 void setSendBufferSize(int size) 将此 Socket 的 SO_SNDBUF 选项设置为指定的值。 static void setSocketImplFactory(SocketImplFactory fac) 为应用程序设置客户端套接字实现工厂。 void setSoLinger(boolean on, int linger) 启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。 void setSoTimeout(int timeout) 启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。 void setTcpNoDelay(boolean on) 启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。 void setTrafficClass(int tc) 为从此 Socket 上发送的包在 IP 头中设置流量类别 (traffic class) 或服务类型八位组 (type-of-service octet)。 void shutdownInput() 此套接字的输入流置于“流的末尾”。 void shutdownOutput() 禁用此套接字的输出流。 String toString() 将此套接字转换为 String
public class ServerSocketextends Object
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。 服务器套接字的实际工作由 SocketImpl 类的实例执行。应用程序可以更改创建套接字实现的套接字工厂来配置它自身,从而创建适合本地防火墙的套接字。
构造方法
ServerSocket() 创建非绑定服务器套接字。 ServerSocket(int port) 创建绑定到特定端口的服务器套接字。 ServerSocket(int port, int backlog) 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 ServerSocket(int port, int backlog, InetAddress bindAddr) 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
主要的api方法
Socket accept() 侦听并接受到此套接字的连接。 void bind(SocketAddress endpoint) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 void bind(SocketAddress endpoint, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 void close() 关闭此套接字。 ServerSocketChannel getChannel() 返回与此套接字关联的唯一 ServerSocketChannel 对象(如果有)。 InetAddress getInetAddress() 返回此服务器套接字的本地地址。 int getLocalPort() 返回此套接字在其上侦听的端口。 SocketAddress getLocalSocketAddress() 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。 int getReceiveBufferSize() 获取此 ServerSocket 的 SO_RCVBUF 选项的值,该值是将用于从此 ServerSocket 接受的套接字的建议缓冲区大小。 boolean getReuseAddress() 测试是否启用 SO_REUSEADDR。 int getSoTimeout() 获取 SO_TIMEOUT 的设置。 protected void implAccept(Socket s) ServerSocket 的子类使用此方法重写 accept() 以返回它们自己的套接字子类。 boolean isBound() 返回 ServerSocket 的绑定状态。 boolean isClosed() 返回 ServerSocket 的关闭状态。 void setPerformancePreferences(int connectionTime, int latency, int bandwidth) 设置此 ServerSocket 的性能首选项。 void setReceiveBufferSize(int size) 为从此 ServerSocket 接受的套接字的 SO_RCVBUF 选项设置默认建议值。 void setReuseAddress(boolean on) 启用/禁用 SO_REUSEADDR 套接字选项。 static void setSocketFactory(SocketImplFactory fac) 为应用程序设置服务器套接字实现工厂。 void setSoTimeout(int timeout) 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 String toString() 作为 String 返回此套接字的实现地址和实现端口。
相比udp通信,tcp的发送端一定要根据客户端做出反应。否则,会报错。一般会报拒绝连接的异常。但是你可能会以为是端口的错误,其实可能并不是,是由于没有理解到tcp通信的特性导致。
package internet;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Tcp_demo {
public static void main(String[] args) throws IOException {
//创建客户端的socket对象
Socket socket = new Socket("192.168.1.1",10000);
//获取输出出流,写数据
OutputStream os = socket.getOutputStream();
os.write("hello,tcp我来了".getBytes());
socket.close();
os.close();
}
}
但是你直接运行这个发送端是绝对会报错的,因为接收端没有,根本不会有什么反应,但是在udp就没什么问题,udp是不需要进行什么反馈,直接进行数据传输就可以。
如果你要对本机进行测试,完全没必要去那样直接指ip口。用方法获取ip地址就可以。
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(),10000);
用到的一个流是输出流
OutputStream getOutputStream() 返回此套接字的输出流。
package internet;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo1 {
public static void main(String[] args) throws IOException {
//绑定到特定端口的服务器
ServerSocket ss = new ServerSocket(10000);
//倾听并接受到此套接字的连接
Socket s = ss.accept();
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String(bys, 0, len);
System.out.println("数据是"+data);
// s.close();
// ss.close();
}
}
就是将上面的客户端和服务器端的程序接起来。
客户端
package internet;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Tcp_demo {
public static void main(String[] args) throws IOException {
//创建客户端的socket对象
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(),10000);
//获取输出出流,写数据
OutputStream os = socket.getOutputStream();
os.write("hello,tcp我来了".getBytes());
socket.close();
os.close();
}
}
服务器端
package internet;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo1 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10000);
Socket s = ss.accept();
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String(bys, 0, len);
System.out.println("数据是"+data);
// s.close();
// ss.close();
}
}
这个进行通讯的话,首先我们需要开启一个服务器端的程序,然后运行客户端。
首先客户端的程序
package internet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Tcp_demo2 {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket(InetAddress.getLocalHost().getHostAddress(), 10086);
//获取输出流
OutputStream os = s.getOutputStream();
os.write("Hello,tcp".getBytes());
//接收服务器反馈
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String(bys, 0, len);
System.out.println("客户端收到反馈:"+data);
//释放资源
// s.close();
}
}
服务器端的
package internet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo2_sever {
public static void main(String[] args) throws IOException {
//创建服务器端Socket
ServerSocket ss = new ServerSocket(10086);
Socket s = ss.accept();
//获取输入流
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String(bys, 0, len);
System.out.println("服务器收到:" +data);
//给出反馈
OutputStream os = s.getOutputStream();
os.write("收到数据".getBytes());
ss.close();
}
}
客户端
package internet;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Tcp_demo3 {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket(InetAddress.getLocalHost().getHostAddress(), 10086);
//实现键盘录入(自己包装的键盘录入)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line= br.readLine())!=null)
{
if("886".equals(line))
{
break;
}
//获取输出流对象
// OutputStream os = s.getOutputStream();
// os.write(line.getBytes());
//一次写字符串
//封装一下字节流对象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bw.write(line);
bw.newLine();
bw.flush();
s.close();
}
}
}
服务器端
package internet;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo3_server {
public static void main(String[] args) throws IOException {
//创建服务器Socket
ServerSocket ss = new ServerSocket(10086);
//监听客户端连接返回对应socket对象
Socket s = ss.accept();
//获取输入流
InputStream is = s.getInputStream();
//字符输入流
InputStreamReader isr = new InputStreamReader(is);
//加入缓冲
BufferedReader br = new BufferedReader(isr);
String line;
while ((line=br.readLine())!=null)
{
System.out.println(line);
}
//释放资源
ss.close();
}
}
需要注意的是,建立通讯的前提是两端都启动,如果一方关闭,就无法正常通讯。
这样主要还是为了复习一下前面学到的io流 客户端
package internet;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Tcp_demo4 {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket(InetAddress.getLocalHost().getHostAddress(), 10086);
//封装输出对象(数据来源于键盘录入)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while((line=br.readLine())!=null)
{
if("886".equals(line))
{
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
}
}
服务端
package internet;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo_server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10086);
//监听客户端连接,返回一个对应的Socket对象
Socket s = ss.accept();
//接收数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//数据写入文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("666.txt"));
String line;
while((line= br.readLine())!=null)
{
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
bw.close();
ss.close();
}
}
具体的是,我们客户端提供的数据来源于文件,然后结合流将文件内容读取,并写入,传到服务器端。(服务器将收到的数据写入文件,如此实现一个上传文件的效果。)
客户端代码
package internet;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class Tcp_demo5 {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket(InetAddress.getLocalHost().getHostAddress(), 10086);
//数据来源文本文件
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\Student01.txt"));
//封装输出流写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
s.close();
}
}
服务器端代码
package internet;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo5_Server {
public static void main(String[] args) throws IOException {
//创建服务端
ServerSocket ss = new ServerSocket(10086);
//监听客户端连接,返回一个对应的Socket对象
Socket s = ss.accept();
//接收数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//将数据写入文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\data.txt"));
String line;
while((line= br.readLine())!=null)
{
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
ss.close();
}
}
基于上次代码的更改
客户端
package internet;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class Tcp_demo5 {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket(InetAddress.getLocalHost().getHostAddress(), 10086);
//数据来源文本文件
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\Student01.txt"));
//封装输出流写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//自定义结束标记
// bw.write("886");
// bw.newLine();
// bw.flush();
s.shutdownOutput();
//接收反馈
BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
String data = brClient.readLine();
System.out.println("服务器反馈:"+data);
br.close();
s.close();
}
}
服务器端
package internet;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo5_Server {
public static void main(String[] args) throws IOException {
//创建服务端
ServerSocket ss = new ServerSocket(10086);
//监听客户端连接,返回一个对应的Socket对象
Socket s = ss.accept();
//接收数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//将数据写入文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\data.txt"));
String line;
while((line= br.readLine())!=null)
{
// if("886".equals(line))
// {
// break;
// }
bw.write(line);
bw.newLine();
bw.flush();
}
//给出反馈
BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bwServer.write("收到文件");
bwServer.newLine();
bwServer.flush();
bw.close();
ss.close();
}
}
这里面有一个主要用到的方法
shutdownOutput public void shutdownOutput() throws IOException禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,则该流将抛出 IOException
原因是如果不这样用的话
服务器会一直等待客户端的输出。既然服务器阻塞了,客户端等待着服务器的输出,也会被阻塞,所以导致客户端和服务端都被阻塞。
这样就得不到想要的结果。
调用Socket.shutdownOutput()方法后,客户端输出的数据都将被发送,并加上 TCP 的正常连接终止序列
虽然代码不多,但是还是有好多细节。
客户端不变
package internet;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class Tcp_demo5 {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket(InetAddress.getLocalHost().getHostAddress(), 10086);
//数据来源文本文件
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\Student01.txt"));
//封装输出流写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//自定义结束标记
// bw.write("886");
// bw.newLine();
// bw.flush();
s.shutdownOutput();
//接收反馈
BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
String data = brClient.readLine();
System.out.println("服务器反馈:"+data);
br.close();
s.close();
}
}
服务器端
package io_demo;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Tcp_demo5_Server_new {
public static void main(String[] args) throws IOException {
//创建服务器Socket对象
ServerSocket ss = new ServerSocket(10086);
//监听客户端连接
while(true)
{
Socket s = ss.accept();
//为每一个客户端开启一个线程
new Thread(new ServerThread(s)).start();
}
}
}
线程类
package io_demo;
import java.io.*;
import java.net.Socket;
public class ServerThread implements Runnable {
private Socket s;
public ServerThread(Socket s) {
this.s = s;
}
@Override
public void run() {
//接收数据写入文本文件
try {
//解决名称冲突
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
int count = 0;
File file = new File("E:\\java_doc\\src\\io_demo\\data+["+count+"].txt");
while (file.exists())
{
count++;
file = new File("E:\\java_doc\\src\\io_demo\\data+["+count+"].txt");
}
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
String line;
while((line=br.readLine())!=null)
{
bw.write(line);
bw.newLine();
bw.flush();
}
//反馈
BufferedWriter bwServer = new BufferedWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())));
bwServer.write("文件上传成功");
bwServer.newLine();
bwServer.flush();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文章在后面遇到问题或者有新的知识点,会继续更新。欢迎指点。