一、什么是Socket编程:
socket是一种最简单的客户机/服务器通信模式。即客户进程向服务器进程发出某种服务请求,服务器响应该请求。如图所示,同常,一个服务器进程会同时为多个客户进程服务,图中的服务器进程B1同时为客户进程A1、A2和B2提供服务。
Socket也称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄。应用程序常通过“套接字”向网络发出请求或应答网络请求。它具有以下几个特点:
①Socket是连接运行在网络上的2个程序间的双向通讯的端点。
②网络通讯实际上Socket的通讯。
③通讯的两端都有Socket,数据在两个Socket间通过IO进行传输。
二、使用Socket进行网络通讯的过程
①服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户端的连接请求。
②客户程序根据服务器程序所在的主机名和端口号发出连接请求。
③如果一切正常,服务器接收连接请求,并获得一个新的绑定到不同端口地址的套接字。
④客户和服务器通过连续读写套接字进行通讯。
一般说来Socket编程又分为基于TCP的Socket编程和基于UDP的Socket编程
三、创建TCP服务端/客户端:
创建TCP服务端
①创建一个ServerSocket对象
②调用accept()方法接收客户端请求
③从Socket中获取IO流
④对I/O流进行读写操作,完成与客户端的交互
⑤关闭I/O流和Socket
创建TCP客户端
①创建一个Socket对象
②从Socket中获取IO流
③对IO流进行读写操作,完成与服务端的交互
④关闭IO流和Socket
强调:客户端和服务端进行数据传输时,客户端的输入流对应服务端的输出流,客户端的输出流对应服务端的输入流。
//服务端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketTest1 {
public static void main(String[] args) throws IOException{
ServerSocket serverSocket = new ServerSocket(8080);//将服务端与8080端口绑定
Socket socket = serverSocket.accept();//监听8080端口
System.out.println("ip:"+socket.getInetAddress());
System.out.println("port:"+socket.getPort());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
String str = null;
while((str = reader.readLine()) != null){
if(str.equals("quit"))//如果接受到的信息是"quit",就退出
break;
System.out.println("get:"+str);
writer.println("server get it");
}
serverSocket.close();
socket.close();
reader.close();
writer.close();
}
}
这种写法的问题是如果同时有多个客户端访问,就会出现排队现象,非常影响效率。所以,我们可以使用线程的知识,每次有客户连接,就在线程中处理该请求。修改后的服务端代码如下
//客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketTest {
public static void main(String[] args)throws IOException {
ServerSocket serverSocket = new ServerSocket(8080,2);
int flag = 0;
System.out.println("server is ready");
while(true){
Socket socket = serverSocket.accept();
System.out.println("Connect:"+socket.getLocalAddress());
System.err.println("client id:"+(++flag));
System.out.println("Client Port:"+socket.getPort());
handleConnection(socket);//在线程中进行处理
}
//serverSocket.close();
}
private static void handleConnection(Socket socket)throws IOException{
new Thread(new Runnable() {
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
String str = null;
while((str = reader.readLine()) != null){
if(str.equals("quit"))
break;
System.out.println("Receive:"+str);
writer.println("Server received");
}
socket.close();
reader.close();
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
四、基于UDP的Socket编程
1.创建发送端:
①建立DatagramSocket对象,该端点建立时,系统会随机分配一个端口,如果不想随机配置,可以手动指定。
②将数据进行Packet包(DatagramPacket)封装,必须要指定目的地址和端口
③通过Socket服务的Send方法将该包发出
④将Socket关闭
2.创建接收端:
①建立DatagramSocket对象,要监听一个端口
②通过Socket的receive方法将数据存入数据包中
③通过数据包DatagramPacket的方法getData()、getAddress()、getPort()获取包中的指定信息。
④将socket关闭
//接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SocketReceiveTest {
public static void main(String[] args) throws IOException{
DatagramSocket socket = new DatagramSocket(8080);//监听8080端口
byte[] bytes = new byte[100];
DatagramPacket packet1 = new DatagramPacket(bytes, bytes.length);
socket.receive(packet1);
System.out.println(new String(packet1.getData()));
String str = "hello smart";
DatagramPacket packet2 = new DatagramPacket(str.getBytes(), str.length(),
InetAddress.getByName("localhost"), packet1.getPort());
socket.send(packet2);//接收到消息后返回内容,端口就是发送端的发送端口。
socket.close();
}
}
//发送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SocketSendTest {
public static void main(String[] args) throws IOException{
DatagramSocket socket = new DatagramSocket();//这里可以不指定本方的发送端口
String str = "hello world";
DatagramPacket packetSend = new DatagramPacket(str.getBytes(), str.length(),
InetAddress.getByName("localhost"), 8080);//指定发送到localhost的8080端口
System.out.println("send port:"+packetSend.getPort());
socket.send(packetSend);
byte[] bytes = new byte[100];
DatagramPacket packetReceive = new DatagramPacket(bytes, bytes.length);
socket.receive(packetReceive);
System.out.println(new String(packetReceive.getData()));
socket.close();
}
}