zoukankan      html  css  js  c++  java
  • TCP程序开发步骤

    TCP是面向连接的协议,需要通信双方首先建立一个连接。因为TCP可靠、稳定的特点,它被应用于大部分场合,但它对系统资源要求比较高。

    TCP协议服务端程序的开发流程如下所示。

    // 初始化Winsock库,获得协议版本
    // 创建服务Socket对象 (指定协议类型,地址族信息)
    // 绑定Socket (将指定的IP,端口绑定给Socket)
    // 开始监听,并且设置监听数量. (开始监听后,客户端就可以连接成功)
    // 开启端口,接收连接
    // 收发数据(利用建立连接的Socket对象进行通信)
    // 关闭Socket连接
    // 终止Winsock库的调用

    开发一个TCP服务端程序,在完成初始化Winsock库创建套接字(Socket)对象两个通用步骤后,还要完成如下步骤。

    (1)绑定套接字到指定IP地址和端口

    无论是使用哪种协议的服务端程序,都要将服务端的IP地址和端口绑定给先前创建的套接字,客户端程序将与之进行通信。绑定套接字的函数是bind,原型如下。

    int bind(
     SOCKET s, //套接字句柄
     const struct sockaddr FAR *name,    //要绑定的地址
     int namelen                         //name所指定的地址长度
    );

    第一个参数s是要绑定地址的套接字句柄,由socket函数返回。

    第二个参数name是指向sockaddr结构体的指针,用来指定套接字所绑定的地址。

    对于TCP、UDP协议,该地址通常是IP地址和端口号,对于TCP、UDP协议使用sockaddr_in结构体代替sockaddr。其定义如下。

    struct sockaddr_in {
        short  sin_family;      //地址家族(地址格式),Windows为AF_INET
        u_short sin_port;       //端口号
        struct in_addr sin_addr; //IP地址
        char  sin_zero[8];     //占位值,通常为0
    };

    第一个成员sin_family同socket函数的af参数。

    第二个成员sin_port指定了TCP或UDP通信服务的端口号。应用程序选择端口号时应该注意,如0~1023由IANA(Internet Assigned Numbers Authorith)管理,保留为公共服务使用,普通用户应用程序应该选择1024以上的端口号。同时需要注意,这里的值使用的是网络字节顺序,而计算机中存储的数字是主机字节顺序。Winsock库提供了一系列转换函数用于两种顺序之间的转换,如下所示。

    u_short htons(u_short hostshort); //将u_short类型由主机字节顺序转换为网络字节顺序
    u_long htonl(u_long hostlong);    //将u_long类型由主机字节顺序转换为网络字节顺序
    u_short ntohs(u_short netshort);  //将u_short类型由网络字节顺序转换为主机字节顺序
    u_long ntohl(u_long netlong);     //将u_long类型由网络字节顺序转换为主机字节顺序

    例如我们要使用端口6000,那么应该是sin_port = htons(6000);

    第三个成员sin_addr用来存储IP地址,它被定义为一个联合来处理整个32位的值,定义如下。

    struct in_addr {
        union {
            struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
            struct { u_short s_w1,s_w2; } S_un_w;
            u_long S_addr;
        } S_un;

    我们知道IP地址由四个部分组成,每个部分由点分开,通常是“xxx.xxx.xxx.xxx”格式。实际上IP地址是一个32位数据,即四个字节构成,每个字节对应由点分开的每个部分。所以每部分的最大值是255。如果直接使用字符串为此成员赋值,通常需要进行转换,Winsock库提供了如下转换函数。

    unsigned long inet_addr(const char  FAR *cp);
    char FAR * inet_ntoa(struct  in_addr in);

    其中inet_addr函数的作用是将用字符串表示的IP地址准换为网络字节顺序的32位值。

    inet_ntoa函数的作用是将用网络字节顺序32位值表示的IP地址转换为用字符串表示的IP地址。

    第四个成员sin_zero是为了与sockaddr结构大小相同而设置的,没有其他含义。

    函数执行成功则返回0,否则返回SOCKET_ERROR,使用WSAGetLastError函数获得错误代码。

    (2)监听连接

    当套接字完成绑定指定IP地址和端口号后就应该设置套接字进入监听状态,也就是监听客户端的连接。使用listen函数,原型如下。

    int listen(
     SOCKET s,   //套接字句柄
     int backlog  //监听队列中允许保持尚未处理的最大连接数量
    );

    当服务端设置套接字进入监听状态,并且排队尚未满的情况下,客户端的连接函数就可以连接成功,否则客户端的连接函数将处理阻塞状态,直到连接成功或超时。

    (3)接受连接请求

    当服务端套接字进入监听状态后,接着应该使用accept函数接受排队等候的连接,如果有排队等候连接则立即返回完成连接,否则accept函数将进入阻塞状态,直到有连接到来,或者socket对象被关闭。accept函数原型如下。

    SOCKET accept(
    SOCKET s, //套接字句柄
    struct sockaddr FAR *addr, //指向sockaddr_in结构的指针,用于接收对方的地址
    int FAR *addrlen     //addr指针指向内存的大小
    );

    该函数在套接字s上取出未处理连接中的第一个连接,然后为这个连接创建新的套接字并返回其句柄,后期的数据的收发就是使用这个新套接字来完成的。

    TCP客户端程序的开发流程如下所示。

    // 初始化Winsock库,获得协议版本
    // 创建服务Socket对象 (指定协议类型,地址族信息)
    // 连接(向指定IP和端口的服务器进行连接)
    // 收发数据(利用建立连接的Socket对象进行通信)
    // 关闭Socket连接
    // 终止对Winsock的调用

    而对于TCP客户端程序的开发,在完成初始化Winsock库创建套接字(Socket)对象两个通用步骤后,接下来就是使用connect函数发起对服务端的连接,函数原型如下。

    int connect(SOCKET s,            //套接字句柄
    const struct sockaddr FAR *name, //指向sockaddr_in结构的指针,指定要连接的服务器的地址 
    int namelen  //addr指针指向内存的大小
    );

    函数执行成功返回0,之后就可以利用套接字s进行收发数据。函数执行失败返回SOCKET_ERROR,使用WSAGetLastError函数进一步获得错误代码。

    无论是TCP协议的服务端程序还是客户端程序,发送数据都是使用send函数,原型如下。

    int send(
     SOCKET s,               //已建立连接的套接字句柄
     const char FAR *buf,    //要发送的内容所在内存首地址
     int len,                //发送内容的长度
     int flags               //指定调用方式,通常置为0
    );

    函数执行成功后返回实际发送数据的字节数。

    接收数据使用recv函数,原型如下。

    int recv(
     SOCKET s,          //已建立连接的套接字句柄
     char FAR *buf,     //要接收的内容所在内存首地址
     int len,           //接收数据缓冲区的长度
     int flags          //指定调用方式,通常置为0
    );

    函数执行成功后返回实际接收数据的字节数。在阻塞模式下,recv将阻塞线程的执行,直至接收到数据。

  • 相关阅读:
    innerHTML与appendChild(newnodeText)的区别
    转]解析C语言中的sizeof
    #pragma 预处理指令详解
    Linux目录结构和常用命令
    Linux下select, poll和epoll IO模型的详解
    重载,继承,重写和多态的区别:
    CentOs7实现lvs+nginx+keepalived负载均衡(DR模式)
    shell中的通配符(grep、cut、sort等)
    kali虚拟机 联网问题解决办法
    MongoDB 数据库
  • 原文地址:https://www.cnblogs.com/cyx-b/p/12535263.html
Copyright © 2011-2022 走看看