Socket实现网络编程

TCP客户端:
1.建立连接套接字,设置Ip和端口监听,socket()
2.建立连接 connect
3.write() 获取网络流对象 发送数据
4.read()获取网络流对象 接收数据
5.关闭套接字

TCP服务器端
1.建立端口监听 socket()
2.绑定指定端口 bind()
3.listen 进行端口监听
4.accept() 阻塞式 直到有客户端访问
5.read()获取客户端发送数据
6.write()发送返回数据
7.close关闭端口监听
在这里插入图片描述
服务器与客户端建立连接所需函数调用

1.socket 函数
socket函数建立一个socket套接字

int socket(int family, int type, int protocol);
返回值:成功返回非负描述符,若出错返回 -1;

int family:协议族 (socket 函数的family常值包括:
 AF_INET, AF_INET6, AF_LOCAL, AF_ROUTE, AF_KEY.

int type:套接字类型(SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET, SOCK_RAM)

int protocol:协议类型(IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP

(1)不是所有的family 和type的组合都是有效的,所以protocol 参数一般设置为0,让内核自动匹配协议类型,避免错误。
(2)地址族AF_XXX。协议族PF_XXX。
(3)POSIX 规范指定 socket函数的第一个参数为PF_ 值,而AF_值用于套接字地址结构。

2.connect 函数
TCP客户用connect 函数来建立与TCP服务器的连接

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); 
返回值:  成功返回 0 ,出错返回 -1。

int sockfd: socket函数返回的套接字描述符。

const struct *servaddr: 指向套接字地址结构的指针。

socklen_t addrlen: 套接字地址结构的的大小。

(1)传递的套接字地址结构中必须含有服务器的IP地址和端口号。
(2)客户在调用connect时候的时候不必非要调用 bind函数,应为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口。
(3)connect 函数将激发TCP的三路握手过程。
(4)connect 发生出错返回的几种情况:

  • 若TCP客户在规定时间内没有收到SYN分节的响应,则返回ETIMEOUT错误
  • 若对客户的SYN响应的是RST(对方连接复位),则表明服务器主机在我们指定的端口上面没有进程在等待与之连接。这是一种硬错误(hard error),客户一收到RST就马上返回ECONNREFUSED 错误。产生RST分节的三个条件是:目的地为某端口的SYN到达,然而在该端口上没有正在监听的服务器;TCP 想取消一个已有连接;TCP接收到一个根本不存在的连接上的分节
  • 若客户发出的SYN在某个中间路由器上引发一个“destination unreachable”(目的地不可达)的ICMP错误,则认为是一种软错误(soft error)客户机在内核中保存该消息。并按某种时间间隔继续发送SYN,在规定的时间内仍未收到回应。则把保存的消息作为 EHOSTUNREACH (ENETUNREACH 错误是过时的 ENETUNREACH 作为 EHOSTUNREACH 对待)错误返回给进程。

(5)如果connect 调用失败,则该套接字描述符不能再用,必须关闭

3. bind 函数
bind函数把一个本地协议地址赋予一个套接字,对于TCP/IP协议,协议地址就是IP地址和端口号的组合。

int bind(int sockfd, cosnt struct sockaddr *myaddr, socklen_t addrlen);
返回值:成功返回0,出错返回 -1

int sockfd: 由socket函数返回的套接字描述符。

const struct sockaddr *myaddr: 指向特定于协议的套接字地址结构的指针。

socklen_t : 套接字地址结构的大小。

捆绑操作涉及三个对象:套接口、地址及端口。其中套接口是捆绑的主题,地址和端口则是捆绑在套接口上的客体
(1)调用bind函数可以同时指定IP地址和端口号, 也可以只指定IP地址或端口号。
(2)除去RPC服务器(使用端口映射器)以外,其他服务器都需要调用bind至少绑定他的服务端口号。
(3)如果客户程序不需要指定特定端口号,那么客户程序一般不调用bind函数,内核将为客户程序分配IP地址和端口号。
(4)如果调用bind函数把一个属于本机某个接口的IP地址绑定到套接字上:对于客户程序来說,这个IP地址就是发送数据报的源IP地址。对于服务器来說,就限制了服务器只能接收目的地为这个IP地址的客户程序的连接。
(5)如果需要绑定的地址被使用 bind 返回一个EADDRINUSE错误。
(6)bind函数并不返回绑定的IP地址和端口号,调用getsockname可以得到这些信息。
(7)bind为不同服务器绑定不同IP ,就可以在同一台多宿主机上运行多个服务器副本,每个副本服务特定的客户。
(8)分组的到达接口和分组的目的地IP地址,强端系统模型和弱端系统

4. listen 函数
listen 函数仅由TCP服务器调用。

int listen(int sockfd, int backlog);返回值:成功返回0 出错返回 -1。

int sockfd: socket 函数返回的套接字描述符。

int backlog: 已连接套接字的已完成连接的最大排队数。

(1)listen 函数把一个未连接的套接字转换成一个被动套接字,指示内核要接受指向该套接字的连接请求。
(2)内核为每个监听套接字维护两个队列:已完成连接队列(incomplete connection queue)、未完成连接队列(completed connection queue)。
(3)未完成连接队列:客户的SYN已经到达服务器,服务器正在等待三路握手(three-way handshake)完成,这样的连接放入未完成连接队列,套接字处于 SYN_RCVD状态。
(4)已完成连接队列:已经成功完成三路握手(three-way handshake)的连接被放入已完成连接队列。套接字处于 ESTABLISHED状态。
(5)backlog 参数指定的是某个给定套接字上内核为之排队的最大已完成连接数。
(6)对已完成连接数做出限制的目的是:在监听某个给定套接字的应用进程(无论什么原因)停止接受连接的时候,防止内核在该套接字上继续接受新的连接请求。
(7)大量未完成连接队列由内核来处理。
(8)当队列是满的,若有新客户的SYN到达TCP就忽略该SYN,一般并不返回任何出错信息。

5. accept 函数
accept函数由TCP服务器调用,用于从已完成连接队列列头返回下一个已完成连接,如果已完成连接队列为空,进程将被投入睡眠(如果套接字为默认的阻塞方式)。

int accept(int sockfd,struct sockaddr *cliaddr, socklen_t *addrlen);
返回值:成功返回非负的描述符,出错返回 -1。

int sockfd : 一个监听套接字。

struct sockaddr *cliaddr : 套接字地址结构的指针,用来返回已连接的对端进程的协议地址。

socklen_t *addrlen: 函数调用的时候是传入的套接字地址结构的大小,函数返回时它的值是内核存放在该套接字地址结构中的确切字节数。

(1)如果accept成功返回,那么它将产生一个新的套接字,称为已连接套接字,用来和客户端进行数据传递的正是这个套接字。
(2)一个服务器通常只创建一个监听套接字(listening socket),它在服务器的整个生存周期内都存在。
(3)内核为每个由服务器进程接受的客户连接创建一个已连接套接字,当服务器完成对某个客户的服务时,相应的套接字将被关闭。
(4)accept 函数最多返回三个值:一个既可能是新的套接字描述符也可能是出错指示的整数、客户进程的协议地址、以及该地址的大小。

6. read()、write()等函数
至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信。

read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。

write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数。失败时返回-1,并设置errno变量。 在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是全部的数据。2)返回的值小于0,此时出现了 错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)。

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

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

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

发布了162 篇原创文章 · 获赞 58 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/ThreeAspects/article/details/105570390
今日推荐