目录
UDP数据报套接字编程
API
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。
DatagramSocket 构造方法:
方法签名 | 方法说明 |
DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端) |
DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定到本机指定端口(一般用于服务器) |
ocket 方法:
方法签名 | 方法说明 |
void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据包,该方法会阻塞等待) |
void send(DatagramPacket p) | 从此套接字发送数据报(不会则色等待,直接发送) |
void close() | 关闭此数据报套接字 |
DatagramPacket是UDP Socket发送和接收的数据报。
DatagramPacket 构造方法:
DatagramPacket 方法:
方法签名 | 方法说明 |
InetAddress getAddress() | 从接受的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 |
int getPort() | 从接受的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机的端口号 |
byte[] getData() | 获取数据报中的数据 |
构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创
建。
InetSocketAddress ( SocketAddress 的子类 )构造方法:
方法签名 | 方法说明 |
InetSocketAddress(InetAddres addr,int port) | 创建一个Socket地址,包含IP地址和端口号 |
代码示例--(回显)单个客户端
UdpEchoServer
public class UdpEchoServer {
//创建一个DatagramSocket对象,后续操作网卡
private DatagramSocket socket=null;
public UdpEchoServer(int port) throws SocketException {
socket=new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
//1.读取请求并解析
DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
//当前完成receive之后,数据是以二进制的形式存储到DatagramPacket中了
//要想能够把这里的数据给显示出来,还需要把这个二进制数据转换成字符串
String request=new String(requestPacket.getData(),0,requestPacket.getLength());
//2.根据请求计算响应
String response=process(request);
//3.把响应写回到客户端
DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
//4.打印
System.out.printf("[%s:%d] req=%s, resp=%s\n",requestPacket.getAddress().toString(),
requestPacket.getPort(),request,response);
}
}
public String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server=new UdpEchoServer(9090);
server.start();
}
}
UdpEchoClient
public class UdpEchoClient {
private DatagramSocket socket=null;
private String serverIp="";
private int serverPort=0;
public UdpEchoClient(String ip,int port) throws SocketException {
//不能指定端口
socket=new DatagramSocket();
//由于Udp自身不会持有对端的信息,就需要把对端的情况给记录下来
serverIp=ip;
serverPort=port;
}
public void start() throws IOException {
System.out.println("客户端启动");
Scanner scanner=new Scanner(System.in);
while(true){
System.out.print("->");
String request=scanner.next();
//把请求内容构造成DatagramPacket对象,发送给服务器
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serverIp),serverPort);
socket.send(requestPacket);
//读取响应
DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
//把响应转换成字符串
String response=new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
// UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);
UdpEchoClient client=new UdpEchoClient("123.249.93.104",9090);
client.start();
}
}
UdpDictServer(词典)
public class UdpDictServer extends UdpEchoServer{
private Map<String,String> dict=new HashMap<>();
public UdpDictServer(int port) throws SocketException {
super(port);
dict.put("dog","小狗");
dict.put("cat","小猫");
dict.put("pig","小猪");
}
@Override
public String process(String request){
return dict.getOrDefault(request,"该词在字典中不存在");
}
public static void main(String[] args) throws IOException {
UdpDictServer server=new UdpDictServer(9090);
server.start();
}
}
将服务端程序部署到云服务器上
打 jar 包
将生成的 jar 包上传到云服务器上,然后使用命令 java -jar netComunicate.jar
TCP流套接字编程
API
ServerSocket 是创建TCP服务端Socket的API。
ServerSocket 构造方法:
方法签名 | 方法说明 |
ServerSocket(int port) | 创建一个服务端流套接字Socket,并绑定到指定端口 |
ServerSocket 方法:
方法签名 | 方法说明 |
Socket accept() | 开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待 |
void close() | 关闭此套接字 |
Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回。
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。
Socket 构造方法:
方法签名 | 方法说明 |
Socket(String host,int port) | 创建一个客户端流套接字Socket,并与对应IP的主机上对应端口的进行建立连接 |
:
方法签名 | 方法说明 |
InetAddress getInetAddress() | 返回套接字所建立的地址 |
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
长短链接
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。
对比以上长短连接,两者区别如下:
建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。
代码示例--(回显)多个客户端
TcpEchoServer
public class TcpEchoServer {
private ServerSocket serverSocket=null;
public TcpEchoServer(int port) throws IOException {
serverSocket=new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
ExecutorService service= Executors.newCachedThreadPool();
while(true){
//通过accept方法,把内核中已经建立好的连接拿到应用程序中
Socket clientSocket=serverSocket.accept();
// Thread t=new Thread(()->{
// processConnection(clientSocket);
// });
// t.start();
service.submit(new Runnable() {
@Override
public void run() {
processConnection(clientSocket);
}
});
}
}
public void processConnection(Socket clientSocket){
System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
try(InputStream inputStream=clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream()) {
//使用 try() 方式,避免后续用完了流对象,忘记关闭
while(true){
Scanner scanner=new Scanner(inputStream);
if(!scanner.hasNext()){
System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
break;
}
//1.读取请求并解析,此处就以 next 作为读取请求的方法,next的规则就是读到"空白符"就返回
String request=scanner.next();
//2.根据请求,计算响应
String response=process(request);
//3.把响应写回客户端
// 可以把String 转换成字节数组,写入到OutputStream
// 也可以使用PrintWriter把OutputStream包裹一下,来写入字符串
PrintWriter printWriter=new PrintWriter(outputStream);
// 此处的println不是打印到控制台了,而是写入到outputStream对应的流对象中
// 也就是写入到clientSocket里面,自然这个数据也就通过网络发送出去了
// 此处使用println带有 \n 也是为了后续客户端这边可以使用 scanner.next来读取数据
printWriter.println(response);
// 此处还应该 刷新缓冲区,如果没有刷新缓冲区,可能数据仍然在内存中,没有被写入网卡
printWriter.flush();
//4.打印一下这次请求交互过程的内容
System.out.printf("[%s:%d] req=%s, resp=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
request,response);
}
} catch (IOException e) {
// throw new RuntimeException(e);
e.printStackTrace();
}finally {
try{
//在这个地方进行clientSocket的关闭
//processConnection就是在处理一个链接,这个方法这行完毕,这个链接也就处理完了
clientSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server=new TcpEchoServer(9090);
server.start();
}
}
TcpEchoClient
public class TcpEchoClient {
private Socket socket=null;
public TcpEchoClient(String serverIp,int serverPort) throws IOException {
socket=new Socket(serverIp,serverPort);
}
public void start(){
Scanner scanner=new Scanner(System.in);
try(InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream()){
PrintWriter writer=new PrintWriter(outputStream);
Scanner scannerNetwork=new Scanner(inputStream);
while(true){
System.out.print("->");
String request=scanner.next();
// 这里使用 println 是为了让请求后面带上换行
// 也就是和服务器读取请求 sacnner.next呼应
writer.println(request);
writer.flush();
String response=scannerNetwork.next();
System.out.println(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client=new TcpEchoClient("127.0.0.1",9090);
client.start();
}
}
标签:UDP,String,TCP,连接,详解,new,客户端,public,Socket
From: https://blog.csdn.net/wmh_1234567/article/details/141202462