网络编程基础
文章目录
Socket 编程
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:
- 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
- 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
- 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
- Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
- 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。
ServerSocket
服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。
构造方法
方法 | 描述 |
---|---|
public ServerSocket(int port) | 创建绑定到特定端口的服务器套接字。没有绑定ip,默认是0.0.0.0:port |
public ServerSocket(int port, int backlog) | 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
public ServerSocket(int port, int backlog, InetAddress address) | 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
public ServerSocket() | 创建非绑定服务器套接字。 |
对于第一个构造函数的解释,可以看到本地的0.0.0.0:6060端口被监听,表示监听本地所有的网卡,即ipconfig调出来的ip地址
创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。
常用方法
方法 | 描述 |
---|---|
public int getLocalPort() | 返回此套接字在其上侦听的端口。 |
public Socket accept() | 侦听并接受到此套接字的连接。 |
public void setSoTimeout(int timeout) | 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 |
public void bind(SocketAddress epoint, int backlog) | 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。backlog 请求的传入连接队列的最大长度。 |
Socket
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。
构造方法
方法 | 描述 |
---|---|
public Socket(String host, int port) | 创建一个流套接字并将其连接到指定主机上的指定端口号。 |
public Socket(InetAddress host, int port) | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
public Socket(String host, int port, InetAddress localAddress, int localPort) | 创建一个套接字并将其连接到指定远程主机上的指定远程端口。 |
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) | 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
public Socket() | 通过系统默认类型的 SocketImpl 创建未连接套接字 |
常用方法
方法 | 描述 |
---|---|
public void connect(SocketAddress host, int timeout) | 将此套接字连接到服务器,并指定一个超时值。 |
public InetAddress getInetAddress() | 返回套接字连接的地址。 |
public int getPort() | 返回此套接字连接到的远程端口。 |
public int getLocalPort() | 返回此套接字绑定到的本地端口。 |
public SocketAddress getRemoteSocketAddress() | 返回此套接字连接的端点的地址,如果未连接则返回 null。 |
public InputStream getInputStream() | 返回此套接字的输入流。 |
public OutputStream getOutputStream() | 返回此套接字的输出流。 |
public void close() | 关闭此套接字。 |
InetAddress
这个类表示互联网协议(IP)地址,Socket编程时较常用的方法
序号 | 方法描述 |
---|---|
static InetAddress getByAddress(byte[] addr) | 在给定原始 IP 地址的情况下,返回 InetAddress 对象。 |
static InetAddress getByAddress(String host, byte[] addr) | 根据提供的主机名和 IP 地址创建 InetAddress。 |
static InetAddress getByName(String host) | 在给定主机名的情况下确定主机的 IP 地址。 |
String getHostAddress() | 返回 IP 地址字符串(以文本表现形式)。 |
String getHostName() | 获取此 IP 地址的主机名。 |
static InetAddress getLocalHost() | 返回本地主机。 |
String toString() | 将此 IP 地址转换为 String。 |
举个例子
InetAddress
// 通过百度的hostname获取它的ip地址
InetAddress address = InetAddress.getByName("www.baidu.com");
byte a[] = address.getAddress();
ByteArrayInputStream in = new ByteArrayInputStream(a);
int b;
while((b = in.read()) != -1) {
System.out.print(b + " ");
}
System.out.println();
System.out.println(address.getHostAddress());
System.out.println(address.getHostName());
// 用百度id地址的address构建InetAddress对象
InetAddress address1 = InetAddress.getByAddress(a);
System.out.println(address1.getHostAddress());
System.out.println(address1.getHostName());
结果
39 156 66 14
39.156.66.14
www.baidu.com
39.156.66.14
39.156.66.14
Socket和ServerSocket
服务端
服务端用ServerSocket等待客户段连接
package NetworkStudy;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server{
private ServerSocket serverSocket;
Server(int port) throws Exception {
serverSocket = new ServerSocket(port);
}
// 持续等待用户连接
public void run() {
while(true) {
try {
Socket server = serverSocket.accept();
DataInputStream in = new DataInputStream(server.getInputStream());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
System.out.println(in.readUTF());
out.writeUTF("再见");
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) throws Exception {
Server server = new Server(6060);
server.run();
}
}
客户端
客户端用Socket创建与服务端的连接
package NetworkStudy;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
public class Client {
private static String servername = "xxxxxxxxx";// 对应服务端监听的ip地址和端口
private static int port = 6060;
public static void main(String args[]) throws Exception {
Socket client = new Socket(servername,port);
DataInputStream in = new DataInputStream(client.getInputStream());
DataOutputStream out = new DataOutputStream(client.getOutputStream());
out.writeUTF("我来连接了");
System.out.println(in.readUTF());
client.close();
}
}
多用户并发
多个客户连接服务器,服务器同时对客户进行回复。
服务端的设计用实现runable接口,每次来一个客户就开辟一个Interaction线程,调用Response类的对象的方法,(这里用了设计模式中单例的思想,我设计的理由是,用单例会节约内存,不用每来一个客户就创建一个对象)。
Java 多线程编程 | 菜鸟教程 (runoob.com)
服务端
Server.java
package NetworkStudy;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server{
private ServerSocket serverSocket;
Server(int port) throws Exception {
serverSocket = new ServerSocket(port);
}
public void run() {
while(true) {
try {
Socket server = serverSocket.accept();
new Interaction(server).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) throws Exception {
Server server = new Server(6060);
server.run();
}
}
Interaction.java
package NetworkStudy;
import java.net.Socket;
public class Interaction implements Runnable {
private Socket socket;
private Thread t;
public Interaction(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
Response response = Response.getInstance();
try {
response.slove(this.socket);
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() {
if(t == null) {
t = new Thread(this);
t.start();
}
}
}
Response.java
package NetworkStudy;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
public class Response {
// 单例实现
private static class ResponseSingle {
private static final Response response = new Response();
}
private Response(){}
public static final Response getInstance() {
return ResponseSingle.response;
}
public void slove(Socket socket) throws Exception{
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
String query = in.readUTF();
if(query.equals("submit")) submit(in, out);
else if(query.equals("connection")) connection(in, out);
else {
out.writeUTF("Error Comand");
}
socket.close();
}
private void submit(DataInputStream in, DataOutputStream out) throws Exception {
out.writeUTF("submit");
}
private void connection(DataInputStream in, DataOutputStream out) throws Exception {
out.writeUTF("connection");
}
}
客户端
模拟10个用户并发执行
package NetworkStudy;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client extends Thread{
private static int port = 6060;
private int id;
Client(int id){
this.id = id;
}
public static void main(String args[]) throws Exception {
for(int i=1;i<=10;i++) {
new Client(i).start();
}
}
@Override
public void run() {
try {
InetAddress inetAddress = InetAddress.getLocalHost();
Socket client = new Socket(inetAddress.getHostName(),port);
DataInputStream in = new DataInputStream(client.getInputStream());
DataOutputStream out = new DataOutputStream(client.getOutputStream());
if(id % 2 == 0) out.writeUTF("connection");
else out.writeUTF("submit");
System.out.println(in.readUTF());
client.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
写的Demo
https://github.com/ddgotxdy/CS_Demo1
本地或者部署到云服务器,用内网穿透也可以测试,IO + 多线程 + CS模式,上传图片。