一、TCP/IP
TCP/IP 即传输控制协议/网间协议是在网络的使用中最基本的通信协议。TCP/IP 传输协议对互联网中各部分进行通信的标准和方法进行了规定。并且,TCP/IP 传输协议是保证网络数据信息及时、完整传输的两个重要的协议。
二、socket
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,可以用它们来开发TCP/IP网络上的应用程序。
在Client/Server通信模式中我们将请求服务的一方称为客户(client),将提供某种服务的一方称为服务器(server),一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“唤醒”并且为客户提供服务—对客户的请求作出适当的反应。
三、一个hello/hi的简单的网络聊天程序
服务端代码:
package socket; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class server { public static void main(String[] args) throws Exception{ ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务端已启动,等待客户端连接..."); Socket socket = serverSocket.accept(); System.out.println("客户端"+socket.getPort()+"已连接"); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(System.in)); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); String msg = null; while ((msg = bufferedReader.readLine()) != null) { System.out.println("客户端发来消息:"+msg); System.out.println("服务端:"); msg = bufferedReader2.readLine(); outputStreamWriter.write(msg+"\n"); outputStreamWriter.flush(); } serverSocket.close(); } }
客户端代码:
package socket; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; public class client { public static void main(String[] args) throws Exception{ Socket socket = new Socket("127.0.0.1", 8888); System.out.println("开始聊天吧!"); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(System.in)); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); String msg = null; System.out.println("客户端:"); while ((msg = bufferedReader2.readLine()) != null) { outputStreamWriter.write(msg+"\n"); outputStreamWriter.flush(); msg = bufferedReader.readLine(); System.out.println("服务端发来消息:"+msg); } socket.close(); } }
运行结果:
客户端:
服务端:
四、对比JAVA函数和Linux Socket API
Linux socket API:
#include#include int socket(int domain, int type, int protocol);
domain 表示使用的底层协议族 , 如PF_INET (TCP/IPv4),PF_INET6 (TCP/IPv6)等;
type 表示指定服务类型,主要有SOCK_STREAM(TCP流服务)和SOCK_DGRAM(UDP数据报)等服务;
protocol 表示具体传输协议,如IPPROTO_TCP、IPPROTO_UDP等;
#include#include int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
sockfd 表示需要命名(绑定)的目标socket文件描述符;
my_addr 表示将socket地址绑定至sockfd,即命名该sockfd;
addrlen 表示该地址的长度;
#includeint listen(int sockfd, int backlog);
sockfd 表示创建的socket 文件描述符;
backlog 表示提示内核监听队列中处于完全连接状态(ESTABLISHED)socket的最大长度,而半连接状态(SYN_RCVD)socket队列的最大长度定义在/proc/sys/net/ipv4/tcp_max_syn_backlog,若队列满,则peer客户端(connect())将收到ECONNREFUSED;
#include#include int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd 表示已监听的socket文件描述符;
addr 表示用来获取远端的socket地址;
addrlen 表示地址的长度;
#include#include int connect(int sockfd, const struct sockaddr * serv_addr, socklen_t addrlen);
sockfd 表示客户端创建的socket文件描述符;
serv_addr 表示服务器端socket地址;
addrlen 表示服务器端socket地址的大小;
#include#include int close(int sockfd);
close函数会将本地sockfd的读写两个方向全部关闭,并将sockfd的引用计数减一;
JAVA API:
server中ServreSocket函数:
ServerSocket serverSocket = new ServerSocket(8888);
追踪ServerSocket函数的调用栈:
结合java.net.serverSocket的API:
可知代码创建套接字同时绑定了指定端口。
client中Socket函数:
Socket socket = new Socket("127.0.0.1", 8888);
追踪Socket函数的调用栈:
结合java.net.socket的API:
可知代码通过给定地址和端口号创建了流套接字,即协议族为TCP。
bind(new InetSocketAddress(bindAddr, port), backlog);
追踪调用栈:
java.net.ServerSocket的API:
java.net.socket的API:
listen(backlog);
追踪调用栈:
serverSocket.accept();
追踪调用栈:
java.net.ServerSocket的API:
socket.close();
serverSocket.close();
追踪调用栈:
五、总结
通过对这几个函数进行追踪分析,并于Linux socket API进行对比,不难看出,在创建一个ServerSocket(JAVA)对象时就相当于进行了一下执行了Linux Socket API中的socket()、bind()、linsten()三个函数,而JAVA API和Linux Socket API中的accpet()和close()函数类似。