内容简介:点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,麻烦点个在看或点个赞,感谢~文章主要梳理于《UNIX 环境高级编程第二版》 第十六章 网络以前都只是在网上搜的能用的例子,对一些参数不是很清楚,这次汇总。而且网络通信还是很常用的通信手段。
点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,麻烦点个在看或点个赞,感谢~
文章主要梳理于《UNIX 环境高级编程第二版》 第十六章 网络 IPC: 套接字
以前都只是在网上搜的能用的例子,对一些参数不是很清楚,这次汇总。而且网络通信还是很常用的通信手段。
UNIX 环境高级编程对 Socket 通信的描述是套接字网络 IPC( 进程间通信 ) ,可以用于计算机间通信也可用于计算机内通信,管道、消息队列、信号量以及共享内存等都是属于计算机内通信的情况。
一、 套接字Api详细介绍
1. 套接字描述符
首先会先到的是文件描述符,对 Linux 一切皆文件的哲学又多懂了一点儿点儿。
套接字是通信端点的抽象。与应用程序使用文件描述符一样,访问套接字需要使用套接字描述符。套接字描述符在UNIX系统是用文件描述符实现的。
#include <sys/socket.h>
int socket (int domain, int type, int protocal);
返回值:成功返回文件(套接字)描述符,出错返回-1
参数 domain( 域 ) 确定通信的特性,包括地址格式。各个域都有自己的格式表示地址,表示各个域的常数都以 AF_ 开头,意指地址族 (address family).
参数type确定套接字的类型,进一步确定通信特征。下图给出了一些类型,但在实现中可以自由增加对其他类型的支持。
参数protocol通常是 0 ,表示按给定的域和套接字类型选择默认的协议。当对同一域和套接字类型支持多个协议时,可以使用 proticol 参数选择一个特定协议。在 A_FINET 通信域中套接字类型 SOCK_STREAM 的默认协议是 TCP( 传输控制协议 ) ; A_FINET 通信域中套接字类型 SOCK_DGRAM 的默认协议是 UDP( 用户数据报协议 ) 。
字节流(SOCK_STREAM)要求在交换数据之前,在本地套接字和远程套接字之间建
立一个逻辑联系。
Tcp : 没有报文界限,提供的是字节流服务 。之前写过 Qt 传输图片的拆包与解包,原因就是如此吧。
调用socket与调用 open 类型,均可获得用于输入、输出的文件描述符。不用的时候记得 close 关闭。
2. 寻址
如何确定一个目标通信进程?
进程的标识有两个部分:计算机的网络地址可以确定网络上与之想要通信的计算机
服务可以确定计算机上的特定进程。
2.1 字节序
在同一台计算机上进程间通信时,一般无需考虑字节序。
TCP/IP协议栈使用大端字节序。有关字节序大家可自行百度。
Linux系统是小端字节序。
2.2 地址格式
地址确定了特定通信域中的套接字端点,地址格式与特定的通信域相关。为使不同格式的地址能够被传入到套接字函数,地址被强转换成通用的地址结构sockaddr表示。
Linux中, sockaddr_in 定义如下:
struct sockaddr_in {
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
其中成员sin_zero为填充字段,必须全部置 0. 所以在网上搜到的例子有使用 bzero.
我目前使用的ubuntu定义如下:
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
还有很多关于地址查询的函数,这里就不一一列举了。
3. 将套接字与地址绑定
使用bind函数将地址绑定到一个套接字上。
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr * addr, socklen_t len);
返回值:成功返回0,出错返回-1
参数socklen_t使用 sizeof 来计算就好了。
对于使用地址的一些限制:
端口号不能小于 1024 ,除非该进程具有相应的特权 ( 即为超级用户 ) 。可见规则总是因人而异,计算机也是如此 ~
对于因特网域,如果指定IP地址为 ADDR_ANY ,套接字端点可以被绑定到所有的系统网络接口。
注意:linux的 man 命令可以查看 api 的详细说明,而且还有例子,也挺不错的。
4. 建立连接
1> connect
如果处理的是面向连接的网络服务(SOCK_STREAM或 SOCK_SEQPACKET) ,在开始交换数据前,需要在请求服务的进程套接字 ( 客户端 ) 和提供服务的进程套接字 ( 服务器 ) 之间建立一个连接。使用 connect.
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
返回值:成功返回0,出错返回-1
诶,这个参数好熟悉呀,和bind函数的参数一模一样呀 ~
当 client 连接 server 时,由于一些原因,连接可能会失败。可以使用 指数补偿 的算法解决,了解一下即可。
2> listen
server调用 listen 来宣告可以接受连接请求:
#include <sys/socket.h>
Int listen(int sockfd, int backlog);
返回值:成功返回0,出错返回-1
参数backlog提供了一个提示,用于表示该进程所要入队的连接请求数量。其值由系统决定,但上限由 <sys/socket.h> 中 SOMAXCONN 指定。
一旦队列满,系统会拒绝多余的连接请求。
3> accept
一旦服务器调用了listen,套接字就能接收连接请求。使用函数 accept 获得连接请求并建立连接。
#include <sys/socket.h>
Int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
返回值:成功返回文件(套接字)描述符,出错返回-1
函数accept所返回的文件描述符是套接字描述符,该描述符连接到调用 connect 的客户端。这个新的套接字描述符和原始套接字 (sockfd) 具有相同的套接字类型和地址族。传给 accept 的原始套接字没有关联到这个连接,而是继续保持可用状态并接受其他连接请求。
如果不关心客户端标识,可以将addr和 len 设置为 NULL, 否则 addr 存放的是连接的客户端的地址。
如果没有连接请求等待处理,accept会阻塞直到有请求到来。另外 server 可以使用 poll 或 select 来等待一个请求的到来。
5. 数据传输
既然将套接字端点表示为文件描述符,那么只要建立连接,就可以使用read和 write 来通过套接字通信。 read 和 write 函数我几乎不用,了解一下即可。
1> send
#include <sys/socket.h>
Int send(int sockfd, const void *buf, size_t nbytes, int flags);
返回值:成功返回发送的字节数,出错返回-1
注意:如果send成功返回,并不一定并表示连接的另一端的进程接收数据。可以保证的是数据已经无误的发送到网络上。
标志我一直用的是0
2> recv
#include <sys/socket.h>
int recv(int sockfd, const void *buf, size_t nbytes, int flags);
返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0, 出错返回-1
仍然一直是0
如果想定位发送者,可以使用recvfrom来得到数据发送者的源地址。
3> recvfrom
#include <sys/socket.h>
int recv(int sockfd, void *restrict buf, size_t len, int flag,
struct sockaddr *restrict addr,
socklen_t *restrict len);
返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0, 出错返回-1
因为可以获得发送者的地址,recvfrom通常用于无连接套接字。否则, recvfrom 等同于 recv 。
二、 小结
这里面再提一个带外数据,感兴趣的同志可以自行百度。
之前写过一个server和 client 的例子,连接如下,可对应本文做对比阅读。
LinuxSocket Server 与 Client 例子
个人觉得这只是套接字的入门,如果一个服务器要连接多个客户端呢?以后有机会和大家一起分享下select的套接字用法。
思考:毕业后的学习与在学校的学习有什么区别呢 ?
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Flutter 完整开发实战详解(十六、详解自定义布局实战)
- 数据结构 1 线性表详解 链表、 栈 、 队列 结合JAVA 详解
- 详解Openstack环境准备
- Java泛型详解
- iOS RunLoop 详解
- Raft协议详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。