Socket通信是计算机之间常用的通信技术,http传输协议底层就是靠它的,它的底层用c++实现,Java中对其进行了重量级的封装。我们先来学习Java中Socket怎么使用,下面例子是传输图片的(从C->S)
服务端
package socket;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Sever {
public static void main(String[] args) throws IOException {
//服务端
//传的参数有很多种,常用的就是这种直接指定端口为9999来进行服务
ServerSocket serverSocket = new ServerSocket(9999);
//这样服务端就会阻塞自己,直到有客户端来进行连接
Socket socket = serverSocket.accept();
//进行活动
byte[] bytes = new byte[1024];
//1.因为要接收,所以从socket中获得input流
InputStream inputStream = socket.getInputStream();
int read = inputStream.read(bytes);
//2.通过文件输出流来从内存存到硬盘
FileOutputStream fileOutputStream =
new FileOutputStream(new File("D://temp.jpg"));
//3.开始接收
while (read!=-1){
fileOutputStream.write(bytes,0,read);
read=inputStream.read(bytes);
}
//关闭,关闭的顺序像栈一样,最早打开的最晚关闭
fileOutputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
客户端
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//客户端
//前面表示ip地址,后面指定连接端口
//host主机不存在时会抛出UnknownHostException异常
Socket socket = new Socket("localhost",9999);
//进行活动
String pngFile = "C:\\Users\\89507\\Desktop\\aaa.png";
//1.获取文件输入流把文件加载到内存
FileInputStream fileInputStream =
new FileInputStream(new File(pngFile));
byte[] bytes = new byte[1024];
//2.打开socket的输出流,准备传输
OutputStream outputStream = socket.getOutputStream();
int read = fileInputStream.read(bytes);
//3.进行传输
while (read!=-1){
outputStream.write(bytes,0,read);
read=fileInputStream.read(bytes);
}
//关闭
outputStream.close();
fileInputStream.close();
socket.close();
}
}
一些小点:
SeverSocket中的.accept()方法具有阻塞特性,即没有客户端来连接的话他不会往下运行。他会返回一个socket对象,服务端与客户端通过操作这个对象来进行传输
Socket中的InputStream.read()方法具有阻塞特性,即如果没读到东西的话,他会一直尝试读取
C/S端那边要传输数据,那边通过socket开启OutputSteam()流,用这个流进行write()方法;另一端则开启InputStream流,用read()方法来读取传来的数据
close()方法:如果调用SocketInputSteam的close方法时,顺便也会把Socket进行close
public void close() throws IOException {
// Prevent recursion. See BugId 4484411
if (closing)
return;
closing = true;
if (socket != null) {
if (!socket.isClosed())
socket.close();
} else
impl.close();
closing = false;
}
在服务端和客户端互传对象的时候:如果服务端先获得ObjectInputStream对象,客户端就要现货的ObjectOutput对象,反之亦然;特别需要注意的一点是:如果两边同时先获得ObjectInputStream对象,客户端会优先获得该对象,这样服务端走到获取对象那一步时会阻塞
SeverSocket类
有这样一个构造方法:
public ServerSocket(int port, int backlog) throws IOException {
this(port, backlog, null);
}
1.其backlog的含义是设置最大等待队列长度,如果队列已满,则拒绝该链接,默认值为50
2.bind()方法使用场景是在新建SeverSocket时没用写端口,这个时候系统会自动选取一个空闲端口,不过我们想让他搞成我们想要的端口时使用
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8888));
那么问题来了,这个InetSocketAddress是什么呢?
他是表示此类实现IP套接字地址(IP地址+端口号)。其职能用于bind操作
3.设置端口是否允许被复用
serverSocket.setReuseAddress(true);
即允许端口复用,设置为false则表明该端口只能特定的给一个client提供服务
Socket类的使用
1.socket类也有bind操作,其操作对象也是InetSocketAddress,给自己设置端口和地址
2.connect,使用来连接sever端(前提是初始化Socket对象的时候没有设置连接目标)。第二个构造方法可以设置timeout
3.有许多可以获取本地或连接到远程Sever端信息的API,如下:
public InetAddress getInetAddress()
public InetAddress getLocalAddress()
public int getPort()
public int getLocalPort()
.....
4.Socket选项TcpNoDelay
public void setTcpNoDelay(boolean on) throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on));
}
作用是启动/禁用TCP_NODELAY(即启用/禁用Nagle算法)
什么是Nagle算法呢?
该算法可以将许多要发送的数据进行本地缓存(这一过程叫Nagling),以减少发送数据来提高网络软件运行效率
其原理是在未确认ACK之前让发送器吧数据送到缓存里,后面的数据也继续放入缓存中,知道得到确认ACK或者直到“攒到”一定大小的数据在发送
其原理是这样子:
Nagle算法使用与大包,高延迟的场合,而对于要求交互素的的B/S或C/S就不合适了。
Socket在创建的时候都是使用Nagle算法的
5.KeepAlive:当其设置为trul是,一段时间内没用任何数据发过来,那么端点就会发送一个ACK包探测对方,探测双方的TCP/IP连接是否有效,否则一方宕机是这边还保存着连接无疑很浪费资源
基于UDP实现的Socket通信
基本实现:(客户端给服务端输入数据)需要注意的是UDP必须得在两台机器上进行运行
import java.io.IOException;
import java.net.*;
public class Sever {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8888);
byte[] bytes = new byte[12];
DatagramPacket datagramPacket = new DatagramPacket(bytes, 10);
socket.receive(datagramPacket);
socket.close();
}
}
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Client {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
socket.connect(new InetSocketAddress("localhost",8888));
String s = "1234567890";
byte[] bytes = s.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
socket.send(datagramPacket);
socket.close();
}
}