下面的分析主要是参阅了计算机网络(谢希仁第7版)进行总结的,从系统调用--->应用编程接口API--->套接字接口来分析Socket的前因后果,固然维基百科上也有对Socket的详细解释html
大多数操做系统使用系统调用的机制在应用程序和操做系统之间传递控制权。对程序员来讲,系统调用和通常程序设计中的函数调用很是类似 java
当某个应用进程启动系统调用时,控制权就从应用进程传递给了系统调用接口,此接口再将控制权传递给计算机的操做系统。操做系统将此调用转给某个内部过程,并执行所请求的操做。内部过程一旦执行完毕,控制权就又经过系统调用接口返回给应用进程。程序员
系统调用接口实际上就是应用进程的控制权和操做系统的控制权进行转换的一个接口,即应用编程接口APIsql
因为TCP/IP协议族被设计成可以运行在多种操做系统的环境下,TCP/IP标准容许系统设计者可以选择有关API的具体实现细节。编程
目前,可供应用程序使用TCP/IP的应用编程接口API的最著名的是套接字接口bash
而套接字不是物理实体,而是一种抽象,套接字是提供应用程序建立和使用的数据结构服务器
当应用进程(客户或者服务器)须要使用网络进行通讯时,必须首先发出socket系统调用,请求操做系统为其建立一个"套接字"。这个调用的实际效果是请求操做系统把网络通讯所须要的一些 系统资源(存储器空间,CPU时间,网络带宽等) 分配给该应用进程。网络
操做系统为这些资源的总和用一个叫作套接字描述符(socket descriptor)的号码(小的整数) 来表示,而后把这个套接字描述符返回给应用进程。数据结构
由图能够看出Socket通讯与TCP/IP协议是分不开的,要使主机A和主机B可以通讯,必须创建Socket链接,创建Socket链接必须经过底层TCP/IP协议来创建TCP链接。多线程
上面就提到,Socket通讯与TCP/IP协议是紧密相关的,关于Socket编程通讯咱们有两种协议能够选择,那就是常见的TCP协议和UDP协议
UDP协议是一种无链接的协议,也称为数据报协议。每次发送数据报时,须要同时发送本机的socket描述符(就是上面所说的套接字描述符)和接收端的socket描述符。因此,每次通讯都要发送额外的数据。
TCP协议是一种有链接的协议,使用应用程序以前,必须先创建TCP链接。因此每次在进行通讯以前那,咱们须要先创建Socket链接,一个socket做为服务端监听请求,一个socket做为客户端进行链接请求。只有双方创建链接好之后,双方才能够通讯。
简单分析二者的区别:
说到这,至于选择哪一种协议,仍是取决于你的使用场景,固然目前见得比较多就是基于TCP协议的Socket通讯。固然一些实时性较高的一些服务,局域网的一些服务用UDP的多一些。
socket编程总的来讲分为3步:创建链接,数据传送,链接释放。固然服务端程序和客户端程序具体步骤有些区别,如上图所示。
这里咱们用java.net包下的ServerSocket类(主要用于服务端)和Socket类(用于创建链接)来实现一个Socket通讯实例
package com.pjmike.Socket;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLOutput;
/** * 服务端 * * @author pjmike * @create 2018-08-12 17:43 */
public class Server {
private ServerSocket serverSocket = null;
private Socket socket = null;
private DataInputStream input = null;
public Server(int port) {
try {
//绑定端口
System.out.println("bind port ...");
serverSocket = new ServerSocket(port);
System.out.println("Server started and waiting a client ..");
//调用accept()方法,提取链接请求
socket = serverSocket.accept();
//通常都是以字节传输
input = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
String line = "";
while (!line.equals("exit")) {
try {
//readUTF()方法须要读取writeUTF()写过来的数据
line = input.readUTF();
System.out.println("recd: " + line);
} catch (IOException o) {
o.printStackTrace();
}
}
//关闭链接
System.out.println("connection closed ...");
input.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server(5000);
}
}
复制代码
package com.pjmike.Socket;
import java.io.*;
import java.net.Socket;
import java.nio.Buffer;
/** * 客户端 * * @author pjmike * @create 2018-08-12 17:52 */
public class Client {
private Socket socket = null;
private DataOutputStream output = null;
private BufferedReader input = null;
public Client(String address, int port) {
try {
//创建链接
socket = new Socket(address, port);
System.out.println("Connected ...");
//从控制台输入信息
input = new BufferedReader(new InputStreamReader(System.in));
output = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
String line = "";
while (!line.equals("exit")) {
try {
line = input.readLine();
System.out.println("客户端输入的是: "+line);
output.writeUTF(line);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
input.close();
socket.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Client client = new Client("localhost", 5000);
}
}
复制代码
客户端
Connected ...
hello world
客户端输入的是: hello world
nihao
客户端输入的是: nihao
exit
客户端输入的是: exit
复制代码
服务端
bind port ...
Server started and waiting a client ..
recd: hello world
recd: nihao
recd: exit
connection closed ...
复制代码
固然有时咱们也须要多个客户端链接到同一个服务端,这也不是什么难事,采用多线程的方式,让服务器循环调用accept()方法,每收到一个客户端请求就开启一个线程来进行处理。
实际上,Socket就是一种进程间的通讯机制,在Java Socket编程中,也是分三步走:创建通讯链路,数据传输,链路关闭。而网络编程也是和Java I/O操做紧密结合在一块儿的,熟悉I/O操做也是必不可少的。