TCP网络编程框架函数

本文中,我们使用一个简单的client/server模型来说明TCP通信过程以及通信过程使用使用函数。

首先我们应该知道客户端和服务器端的函数使用过程:

客户端:调用socket函数--->调用connect函数请求连接--->调用send发送数据--->调用recv接受数据

服务端:调用socket函数--->调用bind将sockfd与服务地址绑定--->调用listen在套接字处监听请求--->迪调用accept处理一个连接请求--->recv接受数据--->send发送数据

(1)socket函数(客户端、服务器端函数)

函数原型:int socket(int domain, int type, int protocol);

所在头文件:<sys/types.h>和<sys/socket.h>

形参:int domain是指地址族,常用的由AF_INET(ipv4)和AF_INET6(ipv6)。注意:这里AF_INET和PF_INET是相通的。

           int type是指定的此次socket的类型,一般取值SOCK_STREAM(TCP),SOCK_DGRAM(UDP)

           int protocol是指使用的协议,注意type与protocol要搭配才行,通常情况下我们将该参数设为0.

返回值:成功,返回一个套接字描述符,相当于一个open函数返回一个文件描述符;

              失败,返回-1;

功能:在调用socket的一方打开一个套接口,相当于打开一个文件,返回值为文件描述符。

注意:socket是在本地创建一个套接口,通信双方都需要创建各自的套接口,通信双方就是通过这个套接口进行数据交换的。

(2)bind函数(服务器端函数)

函数原型:int bind(int sockfd, struct sockaddr *local_addr, int addrLength);

所在头文件:<sys/types.h>和<sys/socket.h>

形参:int sockfd套接口的描述符

           struct sockaddr*local_addr是本机的sockaddr结构体指针,通常先获得sockaddr_in,然后在使用类型转换为sockaddr

            int addrLength是sockaddr结构体大小,通常使用sizeof(sockaddr)获得即可

返回值:成功,返回0;

              失败,返回,-1;

功能:bind函数是将一个计算机上的套接口与自己的某个地址绑定起来,以使得在后期如果某个程序想要与这个地址所代表的服务通信时直接使用通过与这个服务绑定的socket交换数据。也就是说通过bind函数将本机上的某个服务与socket描述符绑定起来,以后访问该服务等都是通过这个套接口交换数据的。

注意:通常情况下TCP通信的客户端不是必须调用bind函数(可以调用也可以不调用),在TCP服务器端必须调用bind函数。bind函数的功能是将本地的socket与本地标示某一个服务的地址绑定在一起,以便后续通过套接口来与服务交换数据。

因为一台服务器通常情况下会有多个网卡,每个网卡对应这个一个IP地址,这样一个服务器就会有很多个IP地址。通常情况下在服务器端使用bind函数时是将xxx套接口与XXXXXX地址上的XX端口号提供的服务绑定在一起,通常XXXXXX地址上的XX端口是封装在sockaddr_in结构体中的。当一个服务器有多个IP时,我们使用sin_addr.s_addr = INET_ANY来表示将这个服务器上的这个端口与所有IP绑定。

question1 客户端没有调用bind函数会怎样?

答:在客户端没有调用bind函数,系统会自动内核会为我们随机分配一个未使用的port(大于1024)将其与自己的IP构成sockaddr_in对象,系统会自动把这个sockaddr_in与socket描述符绑定。当然我们也可以在客户端调用bind 函数将socket描述符与某个地址绑定,但是必须确保这个端口号没有被占用。

question 2为什么客户端不是必须调用bind 函数,而服务器端必须调用bind 函数?

答:服务器端存放着很多服务,每一个服务使用一个端口号来标记。当客户端想要使用某个服务时,必须要知道该服务所在的主机IP和port,因此要将该这个IP和port与socket描述符绑定。

而客户端只是不用显示的调用bind函数,实际上内核中将客户端IP和一个随机的prot与socket描述符绑定了。因为客户端并不会像服务器端那样使用具体的端口号来指明某种服务。(如http服务端口号为80)。如果不调用bind,执行两次同一个客户端程序,那么中内核为我们分配的客户端prot是不一样的。

(3)connect函数(客户端函数)

函数原型:int connect(int sockfd, struct sockaddr* server_addr, int addrLength);

所在头文件:<sys/socket.h>和<sys/types.h>

形参:int sockfd一个套接口的描述符。

           socketaddr*server_addr服务器端一个sockaddr结构体指针。通常情况下,首先获得sockaddr_in然后将类型转换为sockaddr*。

           int addrLength是结构体sockaddr的大小,一般使用sizeof(sockaddr)获得。

返回值:成功,返回0;

              失败,返回-1;

功能:bind函数将本地的sockfd与本地主机的地址绑定在一起了。connect函数是本地的sockfd与服务器端的地址(server_addr)发起连接请求。connect就是三次握手与服务器端建立TCP连接的一个过程。

connect函数是在客户端调用的,服务器端不用调用connect函数。

注意:如果connect函数调用失败,随之这个套接口(sockfd)也会失效,我们必须使用close函数将sockfd关闭。如果接下来我们想要重新调用connect函数就必须调用socket函数,重新打开一个新的sockfd。

(4)listen函数(服务器端函数)

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

所在头文件:<sys/socket.h>和<sys/types.h>

形参:int sockfd是之前使用socket函数打开的套接字描述符,因为已经使用bind 函数将服务器上的某个服务port和IP与这个套接字绑定在一起,那么我们只需对这个套接字进行简体即可。这个参数又叫做“监听套接字”,只用监听是否有客户端请求到来。

           int backlog是指请求这个“监听套接字”的请求队列长度。因为可能会有很多客户端想要使用服务器上sockfd所绑定的服务,当服务器正在处理一个请求时,将其他的请求放入到请求队列中。

backlog = 未完成的TCP连接个数 + 已完成的TCP连接个数,其中未完成的TCP连接是指client已经发送SYN报文,但是没有完成三次握手。已完成连接是指已经完成了三次握手。

返回值:成功,返回0;

              失败,返回SOCKET_ERROR,可以使用WSAGetLastError获取错误代码;

功能:listen函数的功能就是监听想要使用套接字描述符sockfd所绑定服务的请求。因为我们在listen之前已经使用bind函数将套接口描述符与一个服务的地址绑定在一起,所以可以监听这个套接字描述符就相当于对该服务的请求的监听。

(5)accept函数(服务器端函数)

函数原型:int accept(int sockfd, strcuct sockaddr * clientAddr, int * addrLength);

所在头文件:<sys/socket.h>

形参:int sockfd是与某一个服务绑定的套接口描述符,与listen的sockfd一样是一个监听套接字。

          sockaddr* clientAddr是一个局部结构体指针,用于获取调用connect函数的客户端的信息。

          int *addrLength是clientAddr所指向的结构体大小。

返回值:返回一个新的套接字,使用该套接字向客户端发送、接受数据。这个套接字称为是“连接套接字”,与之前的“监听套接字”不同。accept函数执行完后,“监听套接字”仍然监听,后续数据接受与传出的由“连接套接字”负责。

功能:从连接请求队列中获取第一个连接请求。(当队列中没有请求的话,如果套接口为阻塞方式的话,accpet函数会阻塞直到有新的连接请求到来;如果套接口为非阻塞方式的话,accept函数返回错误代码)并且处理这个请求返回一个新的连接套接字,后续使用这个新的套接字进行数据的发送与接收。

注意:1. accept函数的返回值是一个新的套接字,建立连接以后使用这个新的套接字进行数据发送与接收。以前的那个套接字仍然处于监听状态;

           2. accept函数的第二个和第三个参数使用来被填充的。用来获取调用connect的客户端的信息。如果不需要这些信息,可以把这个两个参数设置为NULL;

           3. 如果请求连接队列中没有“连接请求”了,那么accpet函数会议阻塞方式等待连接请求的到来;如果连接请求队列为空且sockfd是非阻塞方式的话,accpet函数就会返回一个错误信息。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值