Socket网络编程详解(TCP)

socket到底是个什么东西,socket是个TCP协议吗?我们平时很多方面都会用到socket,但确定真的了解socket吗?

一.说起Socket我们在说什么?

Wikipedia:A network socket is an endpoint of a connection in a computer network. In Internet Protocol (IP) networks, these are often called Internet sockets. It is a handle (abstract reference) that a program can pass to the networking application programming interface (API) to use the connection for receiving and sending data. Sockets are often represented internally as integers.

个人理解:socket其实就是一根通信电缆两端的电话终端,电话接通后就相当两个socket建立了连接,两个电话之间可以相互通话,两个socket之间就可以实时收发数据,socket仅仅是一个通信工具,通信工具,通信工具重要的事说三遍(OSI模型中的第四层传输层的API接口,这一层通常使用两种协议TCP或UDP来传输)并不是一种协议。TCP、UDP、HTTP才是我们通常理解的协议。

也就是说,Socket这个工具一般使用TCP和UDP两种协议来通信,否则光杆socket并没有毛用。其实我们所认识到的互联网中的各种通信:web请求、即时通讯、文件传输和共享等等底层都是通过Socket工具来实现的,所以说互联网一切皆socket。搞懂了socket你就相当于打通了任督二脉。

二.Socket的8个必备函数

socket并不可怕,我们只需掌握下面几个C语言socket函数个人觉得就够用了。

1.    int socket(int domain, int type, int protocol);

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符。

socket函数的三个参数和return分别为:

domain:即协议域,又称为协议族(family)。通常我们只需关心这两个协议族就够了AF_INET、AF_INET6。AF_INET表示创建IPv4的socket,那么AF_INET6就表示创建IPv6的socket。

type:指定socket类型。常用的socket类型有,通常我们只需关心SOCK_STREAM、SOCK_DGRAM这两个类型也就够了,SOCK_STREAM表示TCP类型的socket,SOCK_DGRAM表示UDP类型的socket。

protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。通常使用中只需记住这个参数设为0就够了。当protocol为0时,会自动选择type类型对应的默认协议。

return:套接口描述字。如果出现错误,它返回-1,并设置errno为相应的值

当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。具体下文详细说明。

2.   int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

正如上面所说bind()函数把一个地址族中的特定地址(IP+Port)赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。作为服务端我们必须给它指定一个端口号,要不然客户端就不知道该连哪个端口了。所以服务器一般初始化问socket必须调用bind()函数绑定地址然后才能listen()而客户端一般初始化完socket并且知道服务器IP地址和端口号就直接可以调用connect()函数进行连接了,不需要绑定自己的地址,因为系统随机给客户端分配的地址(IP+Port)已经默默发送到服务器了。

好了说了这么多,该说说三个参数及return的含义了:

sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。

addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:

struct sockaddr_in {

              sa_family_t    sin_family; /* address family: AF_INET */

              in_port_t      sin_port;  /* port in network byte order */

              struct in_addr sin_addr;  /* internet address */

};

addrlen:对应的是地址的长度。

return:成功返回0,失败返回-1

3.   int listen(int sockfd, int backlog);

作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

sockfd:即为要监听的socket描述字

backlog:相应socket可以排队的最大连接个数。

return:成功返回0,失败返回-1

4.   int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

作为客户端初始化完socket,不需要bind()就直接可以connect()与服务端建立连接了。因为系统会自动生成一个随机的地址(具体应该为本机IP+随机端口号)。

sockfd:还没绑定客户端具体地址的socket描述字

addr:即将要连接到服务端的地址(IP+port)

addrlen:地址长度

return:如果是阻塞连接,成功立即返回0,如果失败,在iOS系统上超时大约一分钟后返回-1

5.  int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,并把会这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

sockfd:已经绑定具体服务端地址的socket描述字。

sockaddr:一般为NULL,这个参数可以理解为interface指定连接必须从哪里来的,比如是localhost、wifi还是以太网卡。NULL为任意方式。

addrlen:sockaddr地址长度

return:成功返回服务器端socket描述字否则错误。

6.  ssize_t write(int fd, const void *buf, size_t count);

服务端与客户端建立了通信接下来就可以实现网络通信了,可以调用网络I/O进行读写操作了,即实现了网络中不同进程之间的通信!

fd:要写入的的socket文件描述符

buf:将要被写入的缓冲区数据

count:被写入的数据长度

return:返回值大于0,表示写了部分数据或者是全部的数据,这样用一个while循环不断的写入数据,但是循环过程中的buf参数和count参数是我们自己来更新的,也就是说,网络编程中写函数是不负责将全部数据写完之后再返回的,说不定中途就返回了!返回值小于0表示出错。代码见第三部分

7. ssize_t read(int fd, void *buf, size_t count);

如果系统事件源有了一个读取信号事件发生,那么我们可以调用read方法读取网络I/O中的数据。

fd:建立连接的socket文件描述符

buf:读取数据后放入的缓冲区

count:缓冲区大小

return:当读取成功时,read返回实际读取到的字节数,这样我们可以用一个while循环不断的读取数据,但是循环过程中的buf参数和count参数是我们自己来更新的,也就是说,网络编程中写函数是不负责将全部数据读取完之后再返回的。如果返回值是0,表示已经读取到文件的结束了,小于0表示是读取错误。代码见第三部分

8.  int close(int fd);

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

三.循环读取和写入代码

循环写入代码

循环读取代码

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

推荐阅读更多精彩内容