最简单的Tcp连接的例子

1.服务器代码

#include <Windows.h>

//ws2_32.lib对应ws2_32.dll,提供了对网络相关API的支持
#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    /*
    typedef struct WSAData
    {
        WORD                    wVersion;
        WORD                    wHighVersion;
        #ifdef _WIN64
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
        #else
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
        #endif
    } WSADATA, FAR * LPWSADATA;
    */
    WSADATA wsaData;

    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
    {
        /*
        WORD MAKEWORD
        (
        BYTE bLow, //指定新变量的低字节序;
        BYTE bHigh //指定新变量的高字节序;
        );
        //typedef unsigned short      WORD;
        WORD temValue = MAKEWORD(0x12, 0x34);    //temValue = 0x3412
        */

        /*
        WSAStartup:返回0为成功

        WSAStartup应该与WSACleanup成对使用
        WSAStartup的功能是初始化Winsock DLL
        本函数必须是应用程序或DLL调用的第一个Windows Sockets函数.
        它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节.
        应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数.
        */

        /*
        wsaData.wVersion = 0x0202
        wsaData.wHighVersion = 0x0202
        wsaData.szDescription = 0x0046f2c4 "WinSock 2.0"
        wsaData.szSystemStatus = 0x0046f3c5 "Running"
        */
        return -1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        /*
        unsigned short value0 = 0x1234;
        unsigned short temvalue0 = LOBYTE(value0);//temvalue0 = 0x0034
        unsigned short temvalue1 = HIBYTE(value0);//temvalue1 = 0x0012

        LOWORD()得到一个32bit数的低16bit
        HIWORD()得到一个32bit数的高16bit
        LOBYTE()得到一个16bit数最低(最右边)那个字节
        HIBYTE()得到一个16bit数最高(最左边)那个字节
        */

        WSACleanup();
        /*
        WSACleanup是来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
        WSACleanup操作成功返回值为0;否则返回值为SOCKET_ERROR
        在一个多线程的环境下,WSACleanup中止了Windows Sockets在所有线程上的操作.
        */
        return -1;
    }

    //typedef UINT_PTR        SOCKET;
    //typedef _W64 unsigned int UINT_PTR, *PUINT_PTR;
    SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
    /*
    socket():第一个参数指明协议族,主要为AF_INET,AF_INET6,AF_LOCAL等
             第二个参数指明套接口类型,主要为SOCK_STREAM,SOCK_DGRAM,SOCK_RAW等
             第三个参数一般为0
    对于windows环境下,在调用该函数之前需首先调用WSAStartup函数完成对Winsock服务的初始化
    成功时,返回一个小的非负整数值,与文件描述符类似。
    失败时,返回INVALID_SOCKET
    */

    //AF_INET(又称 PF_INET)是 IPv4 网络协议的套接字类型,AF_INET6 则是 IPv6 的;而 AF_UNIX 则是 Unix 系统本地通信

    //#define INVALID_SOCKET  (SOCKET)(~0)
    if (INVALID_SOCKET == sockfd)
    {
        WSACleanup();
        return 0;
    }

    //typedef struct sockaddr_in SOCKADDR_IN;
    /*
    struct sockaddr_in
    {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
    };
    typedef struct in_addr
    {
        union
        {
            struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
            struct { USHORT s_w1,s_w2; } S_un_w;
            ULONG S_addr;
        } S_un;
        #define s_addr  S_un.S_addr            // can be used for most tcp & ip code
        #define s_host  S_un.S_un_b.s_b2    // host on imp
        #define s_net   S_un.S_un_b.s_b1    // network
        #define s_imp   S_un.S_un_w.s_w2    // imp
        #define s_impno S_un.S_un_b.s_b4    // imp #
        #define s_lh    S_un.S_un_b.s_b3    // logical host
    } IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
    */
    SOCKADDR_IN serAddr = {0};

    //#define AF_INET    2    // internetwork: UDP, TCP, etc.
    serAddr.sin_family = AF_INET;            

    //u_short PASCAL FAR htons (__in u_short hostshort);
    //htons 将主机的无符号短整形数转换成网络字节顺序
    //字节序是大端在前还是小端在前是取决于cpu的而不是取决于操作系统的,本机为小端模式
    //网络字节序:就是大端字节序。规定不同系统间通信一律采用网络字节序。
    //unsigned short nvalue = htons(9000);    //nvalue = 10275  9000为0x2328 10275为0x2823
    serAddr.sin_port = htons(9000);
    /*
    htonl()--"Host to Network Long"
    ntohl()--"Network to Host Long"
    htons()--"Host to Network Short"
    ntohs()--"Network to Host Short"
    */

    //inet_addr()的功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)
    //serAddr.sin_addr.S_un.S_addr = 0x0100007f  即1 0 0 127
    serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");        

    /*
    struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。
    二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。

    typedef struct sockaddr
    {
        #if (_WIN32_WINNT < 0x0600)
        u_short sa_family;
        #else
        ADDRESS_FAMILY sa_family;           // Address family.
        #endif //(_WIN32_WINNT < 0x0600)

        CHAR sa_data[14];                   // Up to 14 bytes of direct address.
    } SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;
    */

    if (SOCKET_ERROR == bind(sockfd, (SOCKADDR*)(&serAddr), sizeof(SOCKADDR)))
    {
        /*
        bind:把一个本地协议地址赋予一个套接字

             第一个参数是一个套接字
             第二个参数是指向特定于协议的地址结构的指针
             第三个参数是该地址结构的长度
        */

        closesocket(sockfd);
        WSACleanup();
        return -1;
    }

    if (listen(sockfd, 10) != 0)
    {
        /*
        用listen()创建套接口并为申请进入的连接建立一个后备日志,然后便可用accept()接受连接了。
        listen()仅适用于支持连接的套接口,如SOCK_STREAM类型的。套接口处于一种“变动”模式,
        申请进入的连接请求被确认,并排队等待被接受。这个函数特别适用于同时有多个连接请求的服务器;
        如果当一个连接请求到来时,队列已满,那么客户将收到一个WSAECONNREFUSED错误。
        如无错误发生,listen()返回0。否则的话,返回-1
        */

        closesocket(sockfd);
        WSACleanup();
        return -1;
    }

    SOCKADDR_IN addrClient;
    int len = sizeof(SOCKADDR);
    SOCKET clientSocket = accept(sockfd, (SOCKADDR*)&addrClient, &len);

    if (INVALID_SOCKET == clientSocket)
    {
        closesocket(sockfd);
        WSACleanup();
        return -1;
    }

    if (send(clientSocket, "1234", 4, 0) <= 0)
    {
        closesocket(sockfd);
        closesocket(clientSocket);
        WSACleanup();
        return -1;
    }

    char buff[1024] = {0};
    if (recv(clientSocket, buff, 4, 0) <= 0)
    {
        closesocket(sockfd);
        closesocket(clientSocket);
        WSACleanup();
        return -1;
    }

    printf("%s\n", buff);
    system("pause");

    //释放套接口描述字, 如无错误发生,则closesocket()返回0。否则的话,返回SOCKET_ERROR错误
    closesocket(sockfd);
    closesocket(clientSocket);
    //注意点:所有打开的SOCKET都需要进行关闭

    WSACleanup();
    return 0;
}

2.客户端代码

#include <windows.h>

#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        return -1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        WSACleanup();
        return -1;
    }

    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == sockClient)
    {
        WSACleanup();
        return -1;
    }

    SOCKADDR_IN sockAddr = {0};
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(9000);
    sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    //connect 成功则返回0,失败返回-1,错误原因存于errno中。
    if (SOCKET_ERROR == connect(sockClient, (SOCKADDR*)&sockAddr, sizeof SOCKADDR))
    {
        //connect在进行连接的时候会默认等待一会,如没有建立连接则返回失败
        closesocket(sockClient);
        WSACleanup();
        return -1;
    }

    char buff[1024] = {0};
    if (recv(sockClient, buff, 4, 0) <= 0)
    {
        closesocket(sockClient);
        WSACleanup();
        return -1;
    }
    printf("%s\n", buff);

    if (send(sockClient, "1234", 4, 0) <= 0)
    {
        closesocket(sockClient);
        WSACleanup();
        return -1;
    }
    system("pause");

    closesocket(sockClient);
    WSACleanup();
    return 0;
}
时间: 2024-11-13 17:15:28

最简单的Tcp连接的例子的相关文章

编写一个简单的TCP服务端和客户端

下面的实验环境是linux系统. 效果如下: 1.启动服务端程序,监听在6666端口上  2.启动客户端,与服务端建立TCP连接  3.建立完TCP连接,在客户端上向服务端发送消息 4.断开连接 实现的功能很简单,但是对于初来乍到的我费了不少劲,因此在此总结一下,如有错点请各位大神指点指点 什么是SOCKET(套接字): 百度的解释是:网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 将计算机比作酒店,那么通过IP寻找主机,就好比通过地址寻址酒店.通过端

libevent (二) 接收TCP连接

libevent 接收TCP连接 Evconnlistener 机制为您提供了侦听和接受传入的 TCP 连接的方法.下面的函数全部包含在`<event2/listener.h>`中. evconnlistener 创建监听对象 struct evconnlistener *evconnlistener_new(struct event_base *base,evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,evutil_s

TCP连接的建立与终止

我们通过一个例子来说说TCP连接的建立与断开: 在这个例子中,客户端发起连接,发送请求,服务端响应请求,然后客户端主动关闭连接. 建立连接的过程: 1.客户端发出段1,SYN表示连接请求,如图中所示,序号是1000,每发送一个数据字节,这个序号就要加1,这样在接收端可以根据序号排出数据包的正确顺序,也可以发现丢包的情况,SYN和FIN位也要占一个序号,从第一段可以看出,这次发送的数据是0(即没有发送数据),但是由于SYN要占一位,因此下次发送数据要从1001开始.mss表示最大段尺寸,如果一个段

TCP连接如何断开连接

我们知道,一个基于TCP/IP的客户端-服务器的程序中,正常情况下,我会是启动服务器使其在一个端口上监听请求,等待客户端的连接:通过TCP的三次握手,客户端能够通过socket建立一个到服务器的连接:然后,两者就可以基于这个socket连接通信了.连接结束后,客户端(进程)会退出:在不需要继续处理客户请求的情况下,服务器(进程)也将退出.而且,当一个进程退出的时候,内核会关闭所有由这个进程打开的套接字,这里将触发TCP的四次挥手进而关闭一个socket连接.但是,在一些异常的情况下,譬如:服务器

win32汇编实现一个简单的TCP服务端程序(WinSock的简单认知应用)

Windows网络编程,相信好多人都知道,但是我们一般都是用其他语言编写,例如C,C++,JAVA,python等等,这些语言都可以,但是汇编语言比较底层,利用它,我们可以更清晰的了解到网络编程的内在部分,这是其他语言不能相比的,好了,废话不多说,这其实就是这次的目的(毕竟水平欠缺,还是先来按照罗云斌老师的WIN32汇编书上的例子加以学习,举一反三吧). 说道网络编程,现在我所接触到的程序开发,工具软件的使用,库等等都是基于Windows平台的,想要了解Windows的网络编程就必须要知道Win

几种TCP连接中出现RST的情况

http://my.oschina.net/costaxu/blog/127394 在TCP协议中RST表示复位,用来异常的关闭连接,在TCP的设计中它是不可或缺的.发送RST包关闭连接时,不必等缓冲区的包都发出去,直接就丢弃缓存区的包发送RST包.而接收端收到RST包后,也不必发送ACK包来确认. 其实在网络编程过程中,各种RST错误其实是比较难排查和找到原因的.下面我列出几种会出现RST的情况. 1 端口未打开 服务器程序端口未打开而客户端来连接.这种情况是最为常见和好理解的一种了.去tel

几种TCP连接中出现RST的情况(转载)

TCP RST 网络 linux 目录[-] 1 端口未打开 2 请求超时 3 提前关闭 4 在一个已关闭的socket上收到数据 总结 参考文献: 应该没有人会质疑,现在是一个网络时代了.应该不少程序员在编程中需要考虑多机.局域网.广域网的各种问题.所以网络知识也是避免不了学习的.而且笔者一直觉得TCP/IP网络知识在一个程序员知识体系中必需占有一席之地的. 在TCP协议中RST表示复位,用来异常的关闭连接,在TCP的设计中它是不可或缺的.发送RST包关闭连接时,不必等缓冲区的包都发出去,直接

你知道 HTTP 是如何使用 TCP 连接的吗?今天我就来告诉你!

1.HTTP 是如何使用 TCP 连接的: 世界上几乎所有的 HTTP 通信都是由 TCP/IP 承载的,TCP/IP 是全球计算机及网络设备都 在使用的一种常用的分组交换网络分层协议集.客户端应用程序可以打开一条 TCP/IP 连 接,连接到可能运行在世界任何地方的服务器应用程序.一旦连接建立起来了,在客户端 和服务器的计算机之间交换的报文就永远不会丢失.受损或失序. 尽管报文不会丢失或受损,但如果计算机或网络崩溃了,客户端和服务器之间的通信仍然会被断开.在这种情况下, 会通知客户端和服务器通

如何在socket编程的Tcp连接中实现心跳协议

心跳包的发送,通常有两种技术 方法1:应用层自己实现的心跳包 由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线:同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用. 方法2:TCP的KeepAlive保活机制 因为要考虑到一个服务器通常会连接多个客户端,因此由用户在应用层自己实现心