linux c socket

socket

在 Linux 下使用 头文件中 socket() 函数来创建套接字,原型为:
int socket(int af, int type, int protocol);
1) af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030:C9B4 :FF12 : 48AA :1A2B

也可以使用 PF 前缀,PF 是“Protocol Family”的简写,它和 AF 是一样的。例如,PF_INET 等价于 AF_INET,PF_INET6 等价于 AF_INET6。
2) type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)

3) protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。

有了地址类型和数据传输方式,还不足以决定采用哪种协议吗?为什么还需要第三个参数呢?
正如大家所想,一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。

使用 IPv4 地址,参数 af 的值为 PF_INET。如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP,因此可以这样来调用 socket() 函数:
int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //IPPROTO_TCP表示TCP协议
这种套接字称为 TCP 套接字。

如果使用 SOCK_DGRAM 传输方式,那么满足这两个条件的协议只有 UDP,因此可以这样来调用 socket() 函数:
int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //IPPROTO_UDP表示UDP协议
这种套接字称为 UDP 套接字。

上面两种情况都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议,如下所示:
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
后面多采用这种简化写法。

协程,epoll

协程是通用的阻塞概念,而epoll是网络io阻塞的相关概念
通过epoll实现协程,属于特定领域应用,但是可以将业务代码与事件循环解耦合
C语言实现的协程据我所知只有三种方式:

  • Protothread为代表利用switch-case语义跳转
  • 以ST为代表不依赖libc的setjmp/longjmp上下文切换
  • 以及依赖glibc的ucontext接口(云风的coroutine)。

第一种最轻,但受限最大,第三种耗资源性能慢(陈皓注:glibc的ucontext接口的实现中有一个和信号有关的系统调用,所以会慢,估计在一些情况下会比pthread还慢),目前看来ST是最好使的。

参考文章

socket()函数用法详解:创建套接字
多进程->多线程->多路复用->非阻塞->协程
高性能网络编程系列

epoll知乎
Linux 上的设备 (device)
针对epoll和协程的疑问?
IO多路复用select/poll/epoll介绍
【协程第一话】协程到底是怎样的存在?
协程与io多路复用

一个“蝇量级” C 语言协程库Protothreads-嵌入式
State Threads框架 回调终结者
C语言ucontext实现协程
协程与Epoll的配合-ucontext
Easy-event Framework