socket如连接linux上的端口号_Linux Socket编程(上)

v2-39c4db22346e3b9d6e1fce8dc405e459_1440w.jpg?source=172ae18b

1、Socket通信流程

  1. 服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket
  2. 服务器为socket绑定ip地址和端口号
  3. 服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开
  4. 客户端创建socket
  5. 客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket
  6. 服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端连接请求
  7. 客户端连接成功,向服务器发送连接状态信息
  8. 服务器accept方法返回,连接成功
  9. 客户端向socket写入信息
  10. 服务器读取信息
  11. 客户端关闭
  12. 服务器端关闭

2、API

创建socket--socket函数

函数原型:

int 

参数说明:

  • domain: 协议域,又称协议族(family)。常用的协议族有 AF_INET 、 AF_INET6 、 AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE 等。协议族决定了 socket 的地址类型,在通信中必须采用对应的地址,如 AF_INET 决定了要用 ipv4 地址(32位的)与端口号(16位的)的组合、AF_UNIX 决定了要用一个绝对路径名作为地址。
  • type: 指定 Socket 类型。常用的 socket 类型有 SOCK_STREAM 、 SOCK_DGRAM 、 SOCK_RAW 、 SOCK_PACKET 、 SOCK_SEQPACKET 等。流式 Socket(SOCK_STREAM)是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用。数据报式 Socket(SOCK_DGRAM)是一种无连接的 Socket,对应于无连接的 UDP 服务应用。
  • protocol: 指定协议。常用协议有 IPPROTO_TCP 、 IPPROTO_UDP 、 IPPROTO_STCP 、 IPPROTO_TIPC 等,分别对应 TCP 传输协议、UDP 传输协议、STCP 传输协议、TIPC 传输协议。
注意:type 和 protocol 不可以随意组合,如 SOCK_STREAM 不可以跟 IPPROTO_UDP 组合。当第三个参数为0时,会自动选择第二个参数类型对应的默认协议。

返回值: 如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET(Linux下失败返回-1)。

套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。

命名socket--bind函数

bind()函数把一个地址族中的特定地址赋给socket。例如对应 AF_INET、AF_INET6 就是把一个 ipv4 或 ipv6 地址和端口号组合赋给socket。

函数原型:

int 

参数说明:

  • socketfd: 一个标识已连接套接口的描述字。
  • address: 是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。
  • address_len: 确定 address 缓冲区的长度。
其中 sockaddr 这个地址结构根据地址创建 socket 时的地址协议族的不同而不同。

返回值:如果函数执行成功,返回值为0,否则为SOCKET_ERROR。

监听socket--listen函数

如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

函数原型:

int 

参数说明:

  • socketfd: 要监听的socket的描述字。
  • backlog: 相应socket可以排队的最大连接个数。

socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

建立连接--connect函数

服务端已经做好被动接受连接的准备,那么客户端需要调用如下函数主动连接服务端,客户端利用connect函数连接服务端,如果连接成功后,其第一个参数sockfd就唯一标示这个连接。

函数原型:

int 

参数说明:

  • socketfd: 客户端socket的描述字。
  • sockaddr: 服务器的socket地址。
  • addrlen: socket地址的长度

接受连接--accept函数

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

函数原型:

int 

参数说明:

  • socketfd: 就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
  • sockaddr: 结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
  • len: 它也是结果的参数,用来接受上述 addr 的结构的大小的,它指明 addr 结构所占有的字节个数。同样的,它也可以被设置为NULL。

返回值:成功返回客户端的文件描述符,失败返回-1。 - 如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。 - accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。

监听套接字:监听套接字是在调用listen函数之后,是服务器开始调用socket()函数生成的,称为监听socket描述字(监听套接字)。 连接套接字:accept函数返回的是已连接socket描述字(一个连接套接字),它代表着网络中已经建立好的socket连接。

一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。 连接套接字socketfd_new 并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd一样的端口号。

数据通信--read()、write()等函数

#include 

read函数是负责从fd中读取内容,返回值如下:

  • 返回值大于0:说明读取成功,返回值是实际所读的字节数
  • 返回值等于0:表示已经读到文件的末尾
  • 返回值小于0:读取失败
如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。

write函数将buf中的nbytes字节内容写入文件描述符fd,返回值说明如下:

  • 返回值大于0:说明写入成功,返回值是实际写入的字节数
  • 返回值小于0:写入失败,并设置errno变量

在网络程序中,当我们向套接字文件描述符写时有俩种可能: 1. write的返回值大于0,表示写了部分或者是全部的数据。 2. 返回的值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)。

关闭连接--close函数

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

函数原型

int 

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

Select 函数说明

Select函数用于判断Socket是否已经关闭

函数原型:

int 

参数说明:

  • nfds: 忽略。
  • readfds: 检查可读性。
  • writefds: 检查可些性。
  • exceptfds: 用于例外数据。
  • timeout: 超时时间,传递NULL无限等待,0立刻返回。

可读性说明:

  1. 有数据可以读入。
  2. 连接已经关闭,重设或者中止。
  3. 加入调用了listen,而且一个连接正在建立,那么accept函数调用会成功。

关于连接已经关闭,重设或者中止的判断: 当一个套接字在调用了select之后具有可读性,那么这个时候我们可以通过调用recv获得数据。如果真的有数据发送过来,那么这个调用会成功。如果是关闭,重设或者中止,那么recv的调用会失败,这个时候通过wsagetlasterror就可以判断连接是否已经中断。

可写性说明:

  1. 有数据可以发出。
  2. 如果已完成了对一个非锁定连接调用处理,连接就会成功。
  3. 对于可写性的检查,最好放在需要写数据的时候进行检查。如果和可读性放在同一个地方进行检查,那么select很可能每次都会因为可写性检查成功而返回。

例外数据说明:

  1. 加入已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
  2. 有带外数据可供读写。

fd_set几个宏的说明:

宏说明FD_SETSIZE定义了fd_set所允许存放套接字的最大个数,默认是64FD_CLR(s,*set)从set中删除套接字sFD_ISSET(s, *set)检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUEFD_SET(s, *set)将套接字s加入集合setFDZERO(*set)将set初始化成空集合

select调用流程:

  1. 使用FDZERO宏,初始化自己感兴趣的每一个fd_set。
  2. 使用FDSET宏,将套接字句柄分配给自己感兴趣的每个fd_set。
  3. 调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,它会修改每个fd_set结构,删除那些不存在待决I/O操作的套接字句柄
  4. 根据select的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决)的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。
  5. 知道了每个集合中“待决”的I/O操作之后,对I/O进行处理,然后返回步骤1,继续进行select处理。

socket编程的其他函数说明

网络字节顺序及其转换函数

  1. 网络字节顺序 每一台机器内部对变量的字节存储顺序不同,而网络传输的数据是一定要统一顺序的。所以对内部字节表示顺序与网络字节顺序不同的机器, 一定要对数据进行转换,从程序的可移植性要求来讲,就算本机的内部字节表示顺序与网络字节顺序相同也应该在传输数据以前先调用数据转换函数, 以便程序移植到其它机器上后能正确执行。真正转换还是不转换是由系统函数自己来决定的。
  2. 有关的转换函数
#include 

IP地址转换

有三个函数将数字点形式表示的字符串IP地址与32位网络字节顺序的二进制形式的IP地址进行转换:

1. 

字节处理函数

Socket地址是多字节数据,不是以空字符结尾的,这和C语言中的字符串是不同的。Linux提供了两组函数来处理多字节数据,一组以b(byte)开头,是和BSD系统兼容的函数,另一组以mem(内存)开头,是ANSI C提供的函数。 以b开头的函数有:

#include 

以mem开头的函数有:

#include 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值