socket 编程(一)——基本概念

在类 Unix 的操作系统中,I/O 操作都是通过读写文件描述符(file descriptor)来进行的。文件描述符是类 Unix 系统中的一个重要概念。socket 操作的是网络 I/O,所以也沿用这种设计思路。socket 有对应的套接字描述符 sockfd。很多用于操作文件描述符 fd 的操作都可以用于操作 sockfd。就如常用的文件操作 write()/read() 就可以用于 sockfd,来实现数据的发送和接收。

在接着介绍 socket 之前,先跳出来了解一下文件描述符

文件描述符(File descriptor,后文用 fd 替代),是一个非负整数。当进程打开或者创建一个文件时,系统内核就会向进程返回一个 fd 用来指代这个文件。对文件读写时,就将 fd 作为参数传递给 write() / read() 函数。

在 Unix shell 中,文件描述符0与进程标准输入相关联,文件描述符1与标准输出相关联,文件描述符2与标准错误相关联。

下面这张图描述了,进程、文件描述符以及文件间的关系:

file-descriptor.png

继续介绍 socket

socket 地址

/*==================================
 * socket 的地址结构,作为 bind(), 
 * connect(),accept() 的参数
 *================================*/
struct sockaddr {
   unsigned short   sa_family;
   char             sa_data[14];
};

/*==================================
 * sockaddr_in 是 IPv4 因特网地址,
 * 是具体的 sockaddr。使用时需要强制
 * 转换成 sockaddr
 * sin_family = AF_INET
 *================================*/
struct sockaddr_in {
   short int            sin_family;
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

/*==================================
 * 4字节的 IPv4 地址
 *================================*/
struct in_addr {
   unsigned long s_addr;
};

这些地址是用于 socket 函数的。sockaddr_in 是专用于 IPv4 通信的。IPv6 的地址结构,这里未列出

socket 的地址分为 4类:

  • AF_INET, IPv4 因特网域, 用于不同 pc 通过网络来通信
  • AF_INET6, IPv6 因特网域
  • AF_UNIX, (AF_LOCAL)UNIX 域, 用于同一台 pc 下不同进程间通信
  • AF_UNSPEC, 未指定

可以看出 socket 不仅可以使用 ip 地址作为 sockaddr,还能使用其他协议族的地址。这正和 socket 的设计目标一致:同样的接口既可以用于计算机间通信还能用于计算机内通信

socket 函数

#include <sys/socket.h>

//建立连接
int socket(int domain, int type, int protocal);
int bind(int sockfd, const struct sockaddr *addr, socklent_t len);
int connect(int sockfd, const struct sockaddr *addr, socklen_t lent);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);

//数据传输
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct      sockaddr *destaddr, socklen_t destlen);
ssize_t sendmsg(int socklen, const struct msghdr *msg, int flags);
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct      sockaddr *restrict addr, socklen_t *restrict addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

//关闭连接
#include <unistd.h>
int close(int sockfd);
#include <sys/socket.h>
int shutdown(int sockfd, int how);

note:

1、strict 关键字, 用于告知编译器, strict 修饰的指针所指向的内容,只能通过这个指针修改。没有其他修改途径,有利于后面的代码优化

上面将 socket 相关函数分为了三类: 建立连接、传输数据、关闭连接。这么分的目的是为了和 TCP 传输的过程相对应。尽管 socket 并不仅仅用于 tcp 传输,这里为了和我们熟知的网络知识关联起来,就以 tcp 连接为例来介绍 socket

对于一个 c/s 模型的服务来说,client 和 server 间的通信可以简化以下两个部分:

client

  • sockfd = socket( AF_INET, SOCK_STREAM, 0 )
  • connect( sockfd, &serv_addr, sizeof(serv_addr) )
  • wirte( sockfd, buffer, strlen(buffer) );

客户端创建一个 socket ,返回一个套接字描述符 sockfd。接着,connect() 函数,向服务器发起连接。这个步骤可以看成,client 和 server 进行 tcp 三次握手。

server

  • sockfd = socket( AF_INET, SOCK_STREAM, 0 )
  • bind(sockfd, &serv_addr, sizeof(serv_addr) )
  • listen(sockfd, 1024);
  • newsockfd = accept(sockfd, &cli_addr, sizeof(cli_addr) );

服务端创建一个 socket,返回一个套接字描述符 sockfd。bind( ) 将服务器的地址和其中一个端口绑定到 sockfd 上。调用 listen( ) ,开始在绑定的端口上监听来自客户端的连接。当有新的客户端连接到来时,调用 accept( ) 创建一个新的套接字描述符 newsockfd , 来处理这个新连接。之前的 sockfd 继续监听是否有新连接。

这个过程,可以与下面这张图对应起来

socket_client_server.jpg

在前文中,介绍了最为典型的 socket 类型 Stream Sockets。stream socket 主要用于 tcp 传输

除此之外,socket 还有其他 3种类型

  • Data gram Sockets ,用于 udp 传输
  • Raw Sockets ,用来访问底层协议,主要用来开发新的协议
  • Sequenced Packet Sockets,和 stream sockets 相似,但是它保留了边界

参考

[1] Unix Socket Tutorial

[2] UNIX高级环境编程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 什么是TCP/IP、UDP? TCP/IP(Transmission Control Protocol/Inter...
    liuboxx1阅读 979评论 0 1
  • 网络模型 物理层 物理层表示的是比特流传输,通常包括串口/COM口、并行/LPT口、USB、网线接口、电话线接口;...
    秋风弄影阅读 703评论 0 2
  • 最近在学习Python看了一篇文章写得不错,是在脚本之家里的,原文如下,很有帮助: 一、网络知识的一些介绍 soc...
    qtruip阅读 2,682评论 0 6
  • 我正在参加怦然心动·邂逅你的11封情书——1111情书交友创作大赛,快来给我写情书吧。 昵称:卜卜君 地点:江苏 ...
    卜卜君阅读 435评论 7 4
  • 沿着家的小径走,只有我一人。 我像抚摸婴儿的面庞一样抚摸着树干,看着它历经岁月洗礼的枝干,细小的疤痕。我想起城市里...
    小琦怪阅读 319评论 0 1