java 套接字 阻塞_深入分析Java Socket 原理之阻塞套接字

本文介绍了Java中的套接字概念,包括其作为传输层与应用层之间桥梁的角色,以及如何通过套接字描述符进行操作。详细阐述了一次基于TCP的网络通信过程,包括服务器的监听和连接、客户端的连接请求、服务器的响应处理和客户端的接收与处理。内容涉及套接字的创建、bind、listen、accept、connect、read和write等关键步骤。
摘要由CSDN通过智能技术生成

0、套接字(Socket)介绍

0.1 套接字是什么?

套接字类似于unix中的管道,我们既可以往里面写数据(套接字发送缓冲区),也可以从中读取数据(套接字接收缓冲区);

套接字工作在传输层与应用层之间,主要是为应用程序提供网络I/O的能力;

Sockets API通过套接字描述符去定位要访问套接字文件; 比如说要写入的套接字的套接字描述符为5,那程序便可以通过描述符5去访问这个套接字文件。

0.2 简单的一次Socket通信的图:

省略一些维护缓冲区的细节:

c1526baf53afc48ecff2e9ef5ea93d20.png

即我们的程序通过fgets()将要传输的数据传入标准输入文件中,并写入套接字发送缓冲区,再经过TCP按照MSS(最大报文长度)等选项分包,再经过IP协议族的转发至服务端的相应的套接字接收缓冲区,然后复制到应用程序可以用于输出的标准输出文件中进行相应的处理。

一、一次基于TCP的网络通信的过程:

1.1 服务器启动,并准备监听:

int listenfd, connfd;

struct sockaddr_inservaddr;

char buff[MAXLINE];

//创建一个TCP套接字并返回一个套接字描述符

listenfd = Socket(AF_INET, SOCK_STREAM, 0);

//设置IPV4的套接字地址结构

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

servaddr.sin_port = htons(13);/* daytime server */

//按照套接字地址结构设置服务器监听信息

Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

//将套接字设置为被动打开,即处于等待客户端发起三次握手的状态

Listen(listenfd, LISTENQ);

复制代码

1.2 客户端构建套接字文件并发起连接:

首先构建套接字

//存储套接字描述符,Sockets API通过此描述符对套接字文件进行操作;

int sockfd;

//使用socket函数创建网际的、面向字节流的套接字(即TCP套接字),并返回套接字描述符

if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

err_sys("socket error");

复制代码

然后构建IPV4的套接字地址结构

//IPV4套接字地址结构,主要用于设置套接字的属性

struct sockaddr_inservaddr;

bzero(&servaddr, sizeof(servaddr));

//设置协议为网际协议,网际协议即为IP协议族

servaddr.sin_family = AF_INET;

//设置要访问的服务器端口号为13

servaddr.sin_port = htons(13);

//argv[1]是点分十进制IP,如192.168.0.1

//inet_pton函数将其转变为适合在网络上传输的二进制流

if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)

err_quit("inet_pton error for %s", argv[1]);

复制代码

然后通过connect()函数连接服务器

//传入的参数1. sockfd为套接字描述符;

//2.3. IPV4套接字地址结构和结构体的长度

if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)

err_sys("connect error");

复制代码

1.3 服务器接受连接并返回响应:

样例是比较简单的服务器,一次只能处理一个连接,再往后一点可以使用fork()函数创建新进程,使用线程代替进程,并使用线程池,或者改用非阻塞模式等等。

监听套接字维护着一个连接队列,主要用于提供给Accept()函数去处理已完成三次握手的套接字的请求;

fca904705152d01b502652fc049f6170.png

//监听套接字以及已连接套接字描述符

intlistenfd, connfd;

//应用进程缓冲区

charbuff[MAXLINE];

time_tticks;

for ( ; ; ) {

//调用Accept()函数处理Connect队列中已完成连接的套接字的请求

//并对已连接套接字描述符进行赋值

connfd = Accept(listenfd, (SA *) NULL, NULL);

ticks = time(NULL);

//向缓冲区中写要返回给客户端的数据

snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));

//将缓冲区中数据通过write()函数系统调用写入到套接字中

Write(connfd, buff, strlen(buff));

//close()函数通过四次挥手关闭与客户端的连接

Close(connfd);

}

复制代码

1.4 客户端接收服务端响应并进行相应处理:

charrecvline[MAXLINE + 1];

//调用`read()函数`读取套接字接收缓冲区,并放入recvline缓冲区中

while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {

recvlinbzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(13);/* daytime server */

if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)

err_quit("inet_pton error for %s", argv[1]);

e[n] = 0;/* null terminate */

//将recvline中的数据放入标准输出文件中,并使用I/O函数fputs()输出结果

if (fputs(recvline, stdout) == EOF)

err_sys("fputs error");

}

复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值