UNIX网络编程若干知识点

前言

本文总结了若干博主学习UNXI网络编程时遇到的疑惑点,以及我个人查找资料后得出的解答,希望对你有所帮助。

1. socket_fd(套接字描述符),socket(套接字),sockaddr_in(套接字地址结构)三者是什么样的关系?

socket: 通信的端点,单纯的socket没有任何信息,只表明在计算机上开了个用于通信的口子。把通信抽象成两个点以及连接两个点的直线,socket就是其中的一个点。
sockaddr_in 给socket赋值的,也就是赋予socket所代表端点的各种属性:IP,端口,协议等。sockaddr_in与socket之间的关系是合作关系,不是包含关系!创建socket后,想要利用它通信的话,就必须赋予它属性,之后便称作两者关联了起来。sockaddr_in与sockaddr的关系看这篇文章:sockaddr和sockaddr_in详解
socket_fd 套接字描述符,通信过程中很多函数都会返回socket_fd用以唯一描述socket。socket_fd与unix系统中的文件描述符很像,本质上都是指向的系统为进程划分出来的缓冲空间,每个socket_fd指向两块缓冲空间:输入缓冲区recvBuf输出缓冲区sendbuf。详见一文彻底搞通TCP之send & recv原理
三者关系碎碎念 定义出socket就是为了通信的,而socket内核只会读/写缓冲区,具体的传输工作还是传输层基于缓冲区完成的,所以用socket_fd可以指向recvBufsentBuf,来唯一表示socket。通信过程中需要的对端地址,端口,协议等信息则储存在sockaddr_in中,可以通过*bind()*绑定socket和sockaddr_in,在connect,accept等函数中,系统也会自动绑定。

2. getsockname() 和 getpeername()的使用方法

getsockname(int sock_fd,struct sockaddr *localaddr, socketlen_t *addrlen)
返回与套接字相关联的本地协议地址。
getsockname(int sock_fd,struct sockaddr *localaddr, socketlen_t *addrlen)
返回与套接字相关联的外地协议地址。具体使用方法参考linux getsockname和getpeername使用

3. recv()和send()中的socket_fd到底填什么?

这个问题是上面这些问题的起点,
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
recv()和send()函数中第一个参数要求填一个socket_fd,那么这个socket填哪一个呢?官方的解释是标识连接的套接字的描述符,这是一个不那么清晰的解释。好了,长话短说,举几个例子解释这个概念:(注意,不是完整代码)

/************客户端例子***************/

// 定义一个socketaddr_in,还记得吗?socket地址结构
struct sockaddr_in serv_addr;

// 定义一个socket
int sock_fd;
sock_fd=socket(AF_INET,SOCK_STREAM,0)

//填充数据,sockaddr_in填充的是服务器的信息
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);

// ->point1

// 连接客户端和服务端
connect(socket,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))

// ->point2

//...

//发送和接受函数
send(sock_fd,...)
recv(sock_fd,...)

上面是一个简单的客户端部分程序,而我们的标识连接的套接字的描述符就就是connect后的socket_fd,那connect之前和之后的socket_fd 有啥区别呢?利用2中的getsockname() 和 getpeername()打印point1point2处的与socket_fd 相关联本地和外部地址得到:

# point1
localaddr: 0.0.0.0:0
peeraddr: 0.0.0.0:0

# point2
localaddr: 127.0.0.1:45670
peeraddr:  127.0.0.1:64399

这应该就很清楚了标识连接的套接字的描述符是啥意思了。
简单讲一下服务器端(注意,也不是完整代码)

/**********服务器端例子**************/

// 定义用于储存服务器和客户端的地址结构(体)
struct sockaddr_in server_sockaddr,client_sockaddr;

// 定义两个socket
int server_fd,conn_fd;
socket_fd=socket(AF_INET,SOCK_STREAM,0)

// 设置server_sockaddr的属性
server_sockaddr.sin_family = AF_INET;//IPv4
server_sockaddr.sin_port = htons(SERVPORT);//端口
server_sockaddr.sin_addr.s_addr = INADDR_ANY;//本主机的任意IP都可以使用
bzero(&(server_sockaddr.sin_zero),8);//填充0

// 将服务的地址结构和套接字绑定
bind(server_fd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))

// 接受客户端连接请求,并将返回值赋值给conn_fd
conn_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size)

//...

send(conn_fd,...)
recv(conn_fd,...)

上述代码中conn_fd是我们说的标识连接的套接字的描述符,同样输出server_fdconn_fd的相关联的本地和外部地址:

# server_fd
localaddr: 0.0.0.0:64399
peeraddr: 0.0.0.0:0

# conn_fd
localaddr: 127.0.0.1:64399
peeraddr: 127.0.0.1:45670

相信应该解释清楚了。
为什么不用server_fd接受accept的返回值? server_fd还要用于监听其他客户端的连接请求。服务器没每ccept一个客户端请求,便开一个子进程处理该客户端的请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值