tcp connect与udp connect

    如果是tcp套接字,调用connect函数将激发tcp的三路握手过程,而且仅在
连接成功或出错时返回,其中出错返回可能有以下几种情况。
    1、若tcp客户没有收到SYN分节的响应,则返回ETIMEDOUT错误,举例来说,调用
connect函数时,BSD内核发送一个SYN,若无响应则等待6s在发送一个,若仍无响应则等待
24s后再发送一个,若总共等了75s后仍未收到响应则返回本错误。
    2、若对客户的SYN的响应是RST(表示复位),则表明该服务器主机在我们指定的端口上
没有进程在等待与之连接(例如服务器进程也许没在运行),这是一种硬错误(hard error),客户
一接收到RST就马上返回ECONNREFUSED错误。
       RST是TCP在发生错误时发送的一种TCP分节,产生RST的三个条件是:目的地为某端口的SYN
到达,然而该端口没有正在监听的服务器;tcp取消一个已有连接;tcp接收到一个根本不存在的连接
上的分节。
    3、若客户发出的SYN在中间某个路由器上引发了一个"destination unreachable"(目的地不可达)
ICMP错误,则认为是一种软错误,客户主机内核保存该消息,并按照第一种情况中所述的时间间隔
继续发送SYN,若在某个规定的时间后仍未收到响应,则把保存的消息作为EHOSTTUNREACH错误返回给
进程。
    connect连接一个不存在的ip时,会超时返回,错误码ETIMEDOUT
    connect连接一个存在的ip,但是并没有一个相应的服务进程存在,则立即返回,错误码就是ECONNREFUSED
    connect连接一个因特网不可达的ip地址,也是超时返回,错误码ETIMEDOUT

    按照TCP状态转换图,connect函数导致当前套接字从CLOSE状态(该套接字从由socket函数创建以来
一直所处的状态)转移到SYN_SENT状态,若成功则在转移到ESTABLISHED状态,若connect失败则该套接字
不再可用,必须关闭,我们不能对这样的套接字再次调用CONNECT函数,当循环调用函数connect为给定
主机尝试各个ip地址有一个成功是,在每次connect失败后,都必须close当前的套接字描述符并重新调用
socket。

下面在贴上一段客户端使用connect连接服务器端的例子(非阻塞)

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc,char **argv)
{
    if(argc < 3){
        fprintf(stderr,"usage: %s <ip> <port>\n",argv[0]);
        exit(-1);
    }
    int socketfd = socket(AF_INET,SOCK_STREAM,0);
    if(socketfd < 0){
        exit(1);
    }
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
    
    int oldFlag = fcntl(socketfd,F_GETFL,0);
    fcntl(socketfd,F_SETFL,oldFlag | O_NONBLOCK);
  bool ret = false;
    int error =-1,len = 0;
    len = sizeof(int);
    if(connect(socketfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) == -1){
        if(errno == EINPROGRESS){
        fd_set wfds,rwfds;
        FD_ZERO(&wfds);
        FD_SET(socketfd,&wfds);

        rwfds = wfds;

        struct timeval tv;
        tv.tv_sec = 2;
        tv.tv_usec = 0;
        int retval = select(socketfd+1,&rwfds,&wfds,NULL,&tv);
        if(retval > 0){
            if(FD_ISSET(socketfd,&wfds) || FD_ISSET(socketfd,&rfds)){
                getsockopt(socketfd,SOL_SOCKET,SO_ERROR,&error,(socklen_t*)&len);
                if(error == 0)
                    ret = true;
                else
                    ret = false;
            }
        }else if(retval == 0){
            printf("timeout\n");
        }
        }
    }else{
        printf("wwwwwwwwwwww\n");
        ret = true;
    }
    fcntl(socketfd,F_SETFL,oldFlag);
    if(!ret){
        close(socketfd);
        fprintf(stderr,"cannot connect to server\n");
        return -1;
    }
    printf("connect to success\n");
    return 0;
}    
下面来看Udp的Connect函数
   udp套接口调用Connect函数,与Tcp连接是大不相同的,没有三路握手过程。
内核只是记录对方的ip地址和端口号,它们包含在传递给connect的套接口地址
结构中,并立即返回给调用进程。
   对于已连接套接口:
   1、我们再不能指定目的ip与端口,发送操作与write或者send。
   2、我们不用recvfrom,改用read,内核只会返回那些来自connect所指定协议地址。。
   3、异步错误由已连接Udp套接口返回给进程。(如果是未连接,无法返回异步错误)
   第3点,思考如下:
   udp客户在单个udp套接口上连续发送三个数据报给三个不同的服务器,然后进入
   recvfrom的循环以读应答。有两个数据包被正确的递送,但第三个没有运行服务器,
   第三个主机就以一个ICMP返回端口不可达错误,此ICMP出错包含引起错误的数据包
   的IP和udp头部,以便其接受者确定是哪个套接口引起的错误,发送三个数据包的客户
   需要知道引起错误的数据包目的地址用来区分哪一个引起了错误,但内核无法将此信息
   返回给进程,recvfrom可以返回的仅有errno值,无法知道哪个引起的此错误,因此作出
   决定,仅在进程已将udp套接字连接到对方后,这些异步错误才返回。

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页