socket编程是作为程序员必须会的,所以这篇博客就专门来讲一讲socket编程的基本实现。
话不多说,直接上实例,然后再分析讲解。
TCP版:
client端:
package day01;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
Socket socket=new Socket("127.0.0.1",4700);
//用户的输入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//写到服务器的输出流
PrintWriter os=new PrintWriter(socket.getOutputStream());
//从服务器过来的输入流
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Client---------------");
String line=br.readLine();
while(!line.equals("stop")) {
//将用户输入的信息,发送给服务器
os.println(line);
//刷新输出流,使信息立即到达服务器
os.flush();
//将客户端传递过来的信息打印出来
System.out.println("server:"+is.readLine());
//为下次的输入做准备,必须放在这里
line=br.readLine();
}
os.close();
is.close();
socket.close();
} catch (Exception e) {
System.out.println("Error:"+e);
}
}
}
客户端首先建立socket实例对象,注意socket是双向连接的,所以这里我们用的是tcp连接。
第二个参数是端口号,端口号要注意,不要和现有的端口号冲突。
然后建立socket对象的输入输出流,最后将从服务器传递过来的数据打印,并且将用户输入的信息传递给服务器
server端:
package day01;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Service {
public static void main(String[] args) {
try {
ServerSocket server=null;
Socket socket=null;
try {
//监听目标端口号
server=new ServerSocket(4700);
} catch (Exception e) {
System.out.println("Error:"+e);
}
System.out.println("server-------------------");
try {
//一旦有请求到来就创建socket对象
socket=server.accept();
} catch (Exception e) {
System.out.println("Error:"+e);
}
String line;
//从客户端过来的输入流
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//写到客户端的输出流
PrintWriter os=new PrintWriter(socket.getOutputStream());
//用户的输入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
System.out.println("Client:"+is.readLine());
line=br.readLine();
while(!line.equals("stop")) {
//将用户的输入传递到客户端
os.println(line);
//立刻刷新输入流,使信息立即传递到客户端
os.flush();
//打印客户端传递过来的信息
System.out.println("Client:"+is.readLine());
//为下次用户的输入做准备,必须放在这里
line=br.readLine();
}
is.close();
os.close();
socket.close();
} catch (Exception e) {
System.out.println("Error:"+e);
}
}
}
服务器端的实现和客户端差不多,但是如果你细心点,你会发现,服务器端有两行是一样的:
他们的作用都是打印从客户端传输过来的数据。为什么会这样呢?把第一个删了行不行?
答案肯定是不行的。如果把第一行删了,你会发现从客户端传递过来的数据不能及时的打印到服务器端。至于原因,我们又需要谈到执行流程了:
首先,客户端输入一个字符串完毕,该字符串会实时传递到服务器端,服务器端也需要实时的显示出来,然后服务器端输入字符串完毕后,该字符串也会实时的传递到客户端。注意第一遍结束后,后面的操作全部都在while循环中进行。
如果删除,服务器端就需要我们输入数据后才能打印,而且后面每次都是如此,达不到数据实时互通的效果。为什么会这样呢?
首先,在服务器执行到readLine()以后,进程就会阻塞,等待用户的输入,即后面的操作就会停止。等到用户输入了信息,服务器端才会打印出客户端传递过来的数据,并且又会执行readLine(),再次阻塞进程。所以下次客户端输入的信息不能及时显示。等到用户在客户端再次输入信息,才能打印数据。后面一直都会如此。
那么,没有删除又是如何保证实时性的呢?
首先,服务器端会执行打印操作,用户输入数据后,执行到打印操作时,因为readLine()每次执行完毕后会自动跳到下一行,之前已经打印了数据,所以此时的输入流中没有数据,故此处会阻塞。等到客户端输入数据完毕后,输入流里有了值,此处进程被唤醒,打印刚刚客户端输入的数据,并等待用户输入。从而保证了交流的实时性。
因此总结一下:
Client端的实现:
1.创建socket连接,需要ip地址和空闲的端口号
2.创建socket对象的输入流和输出流,用来和服务器端进行交互
3.进行业务,注意及时把数据放进输出流并立马刷新
4.关闭连接
Service端的实现:
1.创建serverSocket对象来监听端口号,一旦有请求到来则创建socket对象
2.创建socket对象的输入输出流
3.执行业务
4.关闭连接
注意:需要先启动客户端然后再启动服务器端
UDP版:
client端:
package day01;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Client {
public static void main(String[] args) throws IOException {
// 创建通信类,专门进行端之间的数据通信
DatagramSocket socket = new DatagramSocket();
// 用户输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String string = br.readLine();
// 定义服务器端的地址
InetAddress address = InetAddress.getByName("127.0.0.1");
// 需要发送的数据
byte[] data = string.getBytes();
// 服务器所在的端口号
int port = 4700;
// 创建数据报
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
// 发送数据报
socket.send(packet);
byte[] data2=new byte[1024];
//创建数据报,用来接受客户端传递的数据
DatagramPacket packet2=new DatagramPacket(data2, data2.length);
//接受数据,在接受之前会阻塞
socket.receive(packet2);
//解析数据包,转换为字符串
String str=new String(data2,0,data2.length);
System.out.println("客户端问我:"+str);
socket.close();
}
}
server端:
package day01;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Service {
public static void main(String[] args) throws IOException {
//创建通信类,指定占用的端口号
DatagramSocket socket=new DatagramSocket(4700);
byte[] data=new byte[1024];
//创建数据报,用来接受客户端传递的数据
DatagramPacket packet=new DatagramPacket(data, data.length);
//接受数据,在接受之前会阻塞
socket.receive(packet);
//解析数据包,转换为字符串
String str=new String(data,0,data.length);
System.out.println("客户端问我:"+str);
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String string=br.readLine();
//根据数据包获得客户端地址
InetAddress address=packet.getAddress();
//根据数据包获得客户端端口
int port=packet.getPort();
//定义要发送的的数据
byte[] data2=string.getBytes();
//创建数据包,包含响应的数据
DatagramPacket packet2=new DatagramPacket(data2, data2.length,address,port);
//响应客户端
socket.send(packet2);
socket.close();
}
}
总结一下:
客户端发送数据:
1.创建通信类DatagromSocket
2.定义服务器地址,发送的数据,端口号
3.创建并封装数据报DatagromPacket
4.发送该报文
客户端接受数据:
1.创建数组缓存
2.创建接受数据报
3.接受数据
4.解析数据
服务器端接受数据:
同客户端接受数据
服务器端发送数据:
1.创建通信类
2.根据接受报文获取客户端地址,客户端端口号
3.定义发送的数据
4.创建并封装数据报
5.发送该报文
可以看出,udp的实现是利用数据报来实现的。其详细步骤在代码中皆有解释,故不再多言。但是对比和tcp可以明显的发现不同,那就是tcp需要客户端先发起请求,服务器端然后响应。udp是可以服务器主动给客户端发送消息的。当然除此之外还有很多的不同,这里就不再赘述了!
参考博客:点击这里