基于TCP的网络编程

 

HTTP 协议, FTP 协议等很多广泛应用的协议均基于 TCP 协议。 TCP 编程主要为 C/S 模式,客户端和服务器之间的程序设计存在较大差异。

编程流程

style='font-size:1.571428571rem' v:shapes="_x0000_i1025">


 

 

服务器调用 socket() bind() listen() 完成初始化后,调用 accept() 阻塞等待,处于监听端口的状态,客户端调用 socket() 初始化后,调用 connect() 发出 SYN 段并阻塞等待服务器应答,服务器应答一个 SYN-ACK 段,客户端收到后从 connect() 返回,同时应答一个 ACK 段,服务器收到后从 accept() 返回。

 

 

建立连接后, TCP 协议提供全双工的通信服务,但是一般的客户端 / 服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从 accept() 返回后立刻调用 read() ,读 socket 就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用 write() 发送请求给服务器,服务器收到后从 read() 返回,对客户端的请求进行处理,在此期间客户端调用 read() 阻塞等待服务器的应答,服务器调用 write() 将处理结果发回给客户端,再次调用 read() 阻塞等待下一条请求,客户端收到后从 read() 返回,发送下一条请求,如此循环下去。

 

 

如果客户端没有更多的请求了,就调用 close() 关闭连接,就像写端关闭的管道一样,服务器的 read() 返回 0 (表明收到 FIN 段),这样服务器就知道客户端关闭了连接,也调用 close() 关闭连接。注意,任何一方调用 close() 后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用 shutdown() 则连接处于半关闭状态,仍可接收对方发来的数据。

 

API 函数

 

 

这些 socket API 函数都在 sys/socket.h 中。

 

 

socket

 

 

原型: int socket(int family, int type, int protocol);

 

 

socket() 打开一个网络通讯端口,如果成功的话,就像 open() 一样返回一个文件描述符,应用程序可以像读写文件一样用 read/write 在网络上收发数据,如果 socket() 调用出错则返回 -1

 

 

bind

 

 

原型: int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

 

 

bind() 的作用是将参数 sockfd myaddr 绑定在一起,使 sockfd 这个用于网络通讯的文件描述符监听 myaddr 所描述的地址和端口号。

 

 

listen

 

 

原型: int listen(int sockfd, int backlog);

 

 

listen() 声明 sockfd 处于监听状态,并且最多允许有 backlog 个客户端处于连接等待状态,如果接收到更多的连接请求就忽略。 listen() 成功返回 0 ,失败返回 -1

 

 

accept

 

 

原型: int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

 

 

服务器调用 accept() 接受连接,如果服务器调用 accept() 时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。 cliaddr 是一个传出参数, accept() 返回时传出客户端的地址和端口号。 addrlen 参数是一个传入传出参数( value-result argument ),传入的是调用者提供的缓冲区 cliaddr 的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。如果给 cliaddr 参数传 NULL ,表示不关心客户端的地址。

 

 

connect

 

 

原型: int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);

 

 

客户端需要调用 connect() 连接服务器, connect bind 的参数形式一致,区别在于 bind 的参数是自己的地址,而 connect 的参数是对方的地址。 connect() 成功返回 0 ,出错返回 -1

 

 

write

 

对套接字进行写入的形式和过程与普通文件的操作方式一致,内核会根据文件描述符的值来查找所对应的属性,当为套接字的时候,会调用相应的内核函数。

 

 

read

 

 

与写入数据类似,使用 read() 函数可以从套接字描述符中读取数据。

 

 

close

 

 

close() 关闭已经打开的 socket 连接,内核会释放相关的资源。

全部评论

相关推荐

点赞 13 评论
分享
牛客网
牛客企业服务