Socket是什么
Socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
----------------以上内容来自百度百科-------------------
使用TCP
服务端代码
public class TCPServer {
// 启动服务
public void start() {
try {
// 1.创建一个服务端的socket
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("服务器等待与客户端的连接...");
int count=0;
while (true) {
// 2.调用accept方法开始监听,等待客户端的连接
Socket client = serverSocket.accept();
count++;
System.out.println("客户端请求次数:"+count);
new reponseClient(client);
}
} catch (IOException e) {
System.out.println("服务器异常:" + e.getMessage());
}
}
// 响应客户端
private class reponseClient extends Thread {
private Socket clinet;
public reponseClient(Socket client) {
this.clinet = client;
start();
}
public void run() {
try {
// 3.获取输入流,并读取客户端信息
InputStream is = clinet.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine()) != null) {
System.out.println("我是服务器,客户端说:" + info);
}
clinet.shutdownInput(); // 关闭输入流
// 4.获取输出流,响应客户端的请求
OutputStream os = clinet.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("欢迎你登录");
pw.flush();
clinet.shutdownOutput(); // 关闭输出流
// 释放资源
br.close();
isr.close();
is.close();
pw.close();
os.close();
} catch (IOException e) {
System.out.println("服务器 run 异常:" + e.getMessage());
} finally {
if (clinet != null) {
try {
clinet.close();
} catch (IOException e) {
System.out.println("服务器 finally 异常:" + e.getMessage());
}
}
}
}
}
public static void main(String[] args) {
System.out.println("***启动服务器***");
TCPServer server = new TCPServer();
server.start();
}
}
客户端代码
public class TCPClient {
public static void main(String[] args) {
System.out.println("**客户端启动**");
try {
// 1.创建客户端Socket,指定服务器地址和端口
Socket socket = new Socket("localhost", 12345);
// 2.获取输出流,向服务端发送信息
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("我是tom,我要登录");
pw.flush();
socket.shutdownOutput(); // 关闭输出流
// 3.获取输入流,并读取服务端信息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine()) != null) {
System.out.println("我是客户端,服务端说:" + info);
}
socket.shutdownInput(); // 关闭输入流
// 4.关系相关资源
br.close();
isr.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (Exception e) {
System.out.println("客户端异常:"+e.getMessage());
}
}
}
先启动server端的代码,然后启动3次客户端代码
客户端控制台如下
**客户端启动**
我是客户端,服务端说:欢迎你登录
服务端控制台如下
***启动服务器***
服务器等待与客户端的连接...
客户端请求次数:1
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:2
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:3
我是服务器,客户端说:我是tom,我要登录
使用UDP
服务端代码
public class UDPServer {
public void start() {
try {
// 1.创建服务端DatagramSocket
DatagramSocket socket = new DatagramSocket(4321);
// 2.创建数据报,用于接收客户端发送的数据
byte[] data = new byte[1024]; // 指定接收的数据包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);
System.out.println("服务器等待与客户端的连接...");
int count = 0;
while (true) {
socket.receive(packet); // 此方法在接收到数据之前,处于阻塞状态
count++;
System.out.println("客户端请求次数:" + count);
new reponseClient(socket, packet); //放在子线程有问题
}
} catch (Exception e) {
System.out.println("服务器异常:" + e.getMessage());
}
}
// 响应客户端
private class reponseClient {
private DatagramSocket server;
private DatagramPacket packet;
public reponseClient(DatagramSocket server, DatagramPacket packet) {
this.server = server;
this.packet = packet;
run();
}
public void run() {
try {
// 3.读取客户端信息
String info = new String(packet.getData(), 0, packet.getLength(), "utf-8");
System.out.println("我是服务器,客户端说:" + info);
// 4.响应客户端的请求
InetAddress address = packet.getAddress();
int port = packet.getPort();
String s = "欢迎你登录";
byte[] data2 = s.getBytes("utf-8");
// 2.创建数据报,包含响应信息
DatagramPacket packet2 = new DatagramPacket(data2,
data2.length, address, port);
// 3.响应客户端
server.send(packet2);
} catch (IOException e) {
System.out.println("服务器 run 异常:" + e.getMessage());
} finally {
}
}
}
public static void main(String[] args) {
System.out.println("***启动服务器-UDP***");
UDPServer server = new UDPServer();
server.start();
}
}
客户端代码
public class UDPClient {
public static void main(String[] args) {
System.out.println("**客户端启动-UDP**");
try {
//1.创建DatagramSocket对象
DatagramSocket client=new DatagramSocket();
//2.定义服务器的地址,端口号,数据
InetAddress address=InetAddress.getByName("localhost");
int port=4321;
String s="我是tom,我要登录";
byte[] data=s.getBytes("utf-8");
//3.创建数据报,包含发送的数据信息
DatagramPacket packet=new DatagramPacket(data, data.length, address, port);
//4.向服务器发送数据报
client.send(packet);
//5.创建数据报,用于接收服务端的响应信息
byte[] data2=new byte[1024];
DatagramPacket packet2=new DatagramPacket(data2, data2.length);
//6.接收服务端的响应数据
client.receive(packet2);
//7.读取数据
String reply=new String(packet2.getData(),0,packet2.getLength(),"utf-8");
System.out.println("我是客户端,服务器说:"+reply);
//8.关闭相关资源
client.close();
} catch (Exception e) {
System.out.println("客户端异常:"+e.getMessage());
}
}
}
同样先启动server端的代码,然后启动3次客户端代码
客户端控制台如下
**客户端启动-UDP**
我是客户端,服务器说:欢迎你登录
服务端控制台如下
***启动服务器-UDP***
服务器等待与客户端的连接...
客户端请求次数:1
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:2
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:3
我是服务器,客户端说:我是tom,我要登录
注意
- 在UDPServer代码中,启动服务放在主线程,响应客户端的代码放在子线程,第一次运行UDPClient的的时候,在服务端拿不到客户端的值。
- 在UDPServer代码中,启动服务放在主线程,响应客户端的代码放在主线程,一切正常。
- 在UDPServer代码中,启动服务放在子线程1,响应客户端的代码放在子线程2,第一次运行UDPClient的的时候,在服务端拿不到客户端的值。
- 在UDPServer代码中,启动服务放在子线程1,响应客户端的代码放在子线程1,一切正常。
但在TCPServer代码中,启动服务放在主线程,响应客户端的代码放在子线程中仍然可以拿到值。
结论,UDP相关的代码一定要放在相同的线程,要么整个在主线程,要么整个在相同的子线程。
TCP在创建soket时,要求服务端和客户端的端口号要一样。
服务端的socket是ServerSocket类,客户端的socket是Socket类
UDP在创建socket时,要求服务端和客户端的端口号不一样,否则会抛地址已被占用的异常。
服务端和客户端的socket都是DatagramSocket
网友评论