zoukankan      html  css  js  c++  java
  • Socket编程详解

    1  计算机网络基础

    1.1 OSI参考模型(略)

    1.2 TCP/IP协议

     传输控制协议/网际协议,TCP/IP将网络分为四层,从高往低一次为应用层-》传输层-》网络层-》数据链路层。其中,传输层协议包括TCP,UDP;网络层协议包括IP、ARP等;

    如图所示:

    注:图片来源http://blog.csdn.net/lizhifeng2009/article/details/8820228

    2  套接字编程基础

    2.1  套接字

    套接字是网络编程的基础,最初由加利福尼亚大学为UNIX开发的网络通信编程接口,为了在WINDOWS 操作系统上使用,微软与第三方厂商共同制定了一套标准,Winsock.。

    套接字实际上是一个指向传输提供者的句柄,Winsock中通过该句柄实现网络通信与管理。

    可以理解为实现网络通信和管理,在应用层与传输层之间的操作接口。对于用户而言,可通过调用SOCKET 相关接口函数,控制传输协议、传输内容等。

    具体如图所示:

    2.1.1 套接字类型

     根据套接字作用不同,分为三类:原始套接字、流式套接字和数据包套接字。

    原始套接字(SOCK_RAW):能够使程序人员对底层的网络机制进行控制,接收的数据包头中含有IP头。

    流式套接字(SOCK_STREAM):提供了双向、有序、可靠的数据传输服务,在通信前需要双方建立连接,TCP协议采用了该套接字。

    数据包套接字(SOCK_DGRAM):与流式套接字相对应,提供双向数据流,但是不能保证数据传输的可靠性、有序性和无重复性。UDP协议采用了该套接字。

    //2.2 套接字I/O模型

    (暂缺)
    2.3 套接字函数

    为了使用套接字进行网络程序开发,Windows操作系统提供了一组套接字函数,使用这些函数,可以实现功能强大的网络编程。常用可分为三大类:初始化库函数(WSAStartup()、WSACleanup())、通信时使用函数(socket()、bind()、listen()、accept()、closesocket()、connect()、recv()、send()、select()、ioctlsocket())和地址转换函数(htons()、htonl()、inet_addr())。其中,TCP的三次握手是通过connect 函数触发。

    其中,WSAStartup()用于初始化动态链接库函数,WSACleanup()用于释放动态链接库初始化时分配的资源。

    注:套接字函数通常封装在Ws2_32.dll动态链接库中,其头文件Winsock2.h提供了套接字函数的原型,库文件Ws2_32.lib提供了Ws2_32.dll文件的输出节,在使用套接字函数前,需包含头文件 ,并链接Ws2_32.lib。

    #include <winsock2.h>
    
    #pragma comment(lib,"ws2_32.lib")

    3  网络编程步骤

    实现网络间通信,需要一个服务器端和一个客户端。这两者之间的具体实现有点不同,大体步骤为:

    3.1 服务器端

    (1)创建套接字:socket()创建套接字,指明传输协议  

    例如:
    sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
    //sListen代表返回的套接字

    //AF_INET为IPV4地址家族
    //SOCK_STREAM为套接字类型 
    //
    IPPROTO_TCP指明传输协议

    (2)构建本地地址信息  

    //构建本地地址信息  
        saServer.sin_family = AF_INET; //地址家族  
        saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序  
        saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址  

    (3)bind()函数把一个地址族中的特定地址赋给socket。

    //绑定
    ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));  
        if (ret == SOCKET_ERROR)  
        {  
            printf("bind() faild! code:%d
    ", WSAGetLastError());  
            closesocket(sListen); //关闭套接字  
            WSACleanup();  
            //return 0;  
        }  

    (4)监听:listen()

     //侦听连接请求  
        ret = listen(sListen, 5);  
        if (ret == SOCKET_ERROR)  
        {  
            printf("listen() faild! code:%d
    ", WSAGetLastError());  
            closesocket(sListen); //关闭套接字  
            //return 0;  
        }  
        

    (5)等待连接:accept()

    accept()函数提取出所监听套接字的等待连接队列中第一个连接请求创建一个新的套接字,并返回指向该套接字的文件描述符;

    函数原型为:int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

    3.2 客户端

    (1)创建套接字

    (2)构建服务器地址

    (3)连接服务器connect()

    4  实例

    下面通过一个简单的客户端/服务器端程序,讲解通信编程的具体实现:

    //服务器端
    // Server.cpp : 定义控制台应用程序的入口点。
    //TCP测试程序的服务器端程序
    #include "stdafx.h"  
    #include <stdio.h>  
    #include <winsock2.h>  
    #pragma comment(lib,"ws2_32.lib") 
    #define SERVER_PORT 5208 //侦听端口  
      
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        WORD wVersionRequested;  
        WSADATA wsaData;  
        int ret, nLeft, length;  
        SOCKET sListen, sServer; //侦听套接字,连接套接字  
        struct sockaddr_in saServer, saClient; //地址信息     
        char *ptr;//用于遍历信息的指针     
        //WinSock初始化  
        wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本  
        ret=WSAStartup(wVersionRequested, &wsaData);  
        if(ret!=0)  
        {  
            printf("WSAStartup() failed!
    ");  
            //return 0;  
        }  
        //创建Socket,使用TCP协议  
        sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
        if (sListen == INVALID_SOCKET)  
        {  
            WSACleanup();  
            printf("socket() faild!
    ");  
            //return 0;  
        }  
        //构建本地地址信息  
        saServer.sin_family = AF_INET; //地址家族  
        saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序  
        saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址  
        
        //绑定  
        ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));  
        if (ret == SOCKET_ERROR)  
        {  
            printf("bind() faild! code:%d
    ", WSAGetLastError());  
            closesocket(sListen); //关闭套接字  
            WSACleanup();  
            //return 0;  
        }  
        
        //侦听连接请求  
        ret = listen(sListen, 5);  
        if (ret == SOCKET_ERROR)  
        {  
            printf("listen() faild! code:%d
    ", WSAGetLastError());  
            closesocket(sListen); //关闭套接字  
            //return 0;  
        }  
        
        printf("Waiting for client connecting!
    ");  
        printf("Tips: Ctrl+c to quit!
    ");  
    
        //阻塞等待接受客户端连接  
        while(1)//循环监听客户端,永远不停止  
        {  
            length = sizeof(saClient);  
            sServer = accept(sListen, (struct sockaddr *)&saClient, &length);  
            if (sServer == INVALID_SOCKET)  
            {  
                printf("accept() faild! code:%d
    ", WSAGetLastError());  
                closesocket(sListen); //关闭套接字  
                WSACleanup();  
                return 0;  
            }    
      
            char sendMessage[]="
    hello client";  //发送信息给客户端  
            send(sServer,sendMessage,strlen(sendMessage)+1,0);  
      
            char receiveMessage[5000];  
            nLeft = sizeof(receiveMessage);  
            ptr = (char *)&receiveMessage;  
            while(nLeft>0)  
            {  
                //接收数据  
                ret = recv(sServer, ptr, 5000, 0);    //非负,成功;-1,失败
                if (ret == SOCKET_ERROR)  
                {  
                    printf("recv() failed!
    ");  
                    return 0;  
                }  
                if (ret == 0) //客户端已经关闭连接  
                {  
                    printf("Client has closed the connection
    ");  
                    break;  
                }  
                nLeft -= ret;  
                ptr += ret;  
            }    
            printf("receive message:%s
    ", receiveMessage);//打印我们接收到的消息。  
              
      
        }   
      //  closesocket(sListen);  
      //  closesocket(sServer);  
      //  WSACleanup();  
        return 0;  
    }  
    //TCP测试程序的客户端程序
    #include "stdafx.h"  
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <winsock2.h>  
    #pragma comment(lib,"ws2_32.lib")
    #define SERVER_PORT 5208 //侦听端口  
      
      
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        WORD wVersionRequested;  
        WSADATA wsaData;            //WSADATA为Windows套接字结构体
        int ret;  
        SOCKET sClient; //连接套接字  
        struct sockaddr_in saServer; //服务器地址信息  
        char *ptr;  
    
        BOOL fSuccess = TRUE;  
        
        //---------------------------------------------------------------
        //WinSock初始化  
        //---------------------------------------------------------------
        wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本  
        ret = WSAStartup(wVersionRequested, &wsaData);  //加载套接字库  
        if(ret!=0)  
        {  
            printf("WSAStartup() failed!
    ");  
            //return 0;  
        }  
        //确认WinSock DLL支持版本2.2  
        if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)  
        {  
            WSACleanup();   //释放为该程序分配的资源,终止对winsock动态库的使用  
            printf("Invalid WinSock version!
    ");  
            //return 0;  
        }  
        
        //创建Socket,使用TCP协议  
        sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
        if (sClient == INVALID_SOCKET)  
        {  
            WSACleanup();  
            printf("socket() failed!
    ");  
            //return 0;  
        }  
      
        //构建服务器地址信息  
        saServer.sin_family = AF_INET; //地址家族  
        saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序  
        saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  
      
        //连接服务器  
        ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer));  
        if (ret == SOCKET_ERROR)  
        {  
            printf("connect() failed!
    ");  
            closesocket(sClient); //关闭套接字  
            WSACleanup();  
            //return 0;  
        }  
      
      
        char sendMessage[]="hahaha!!!";   
        ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0);  
        if (ret == SOCKET_ERROR)  
        {  
            printf("send() failed!
    ");  
        }  
        else  
            printf("client info has been sent!");  
        char recvBuf[100];  
        recv(sClient,recvBuf,100,0);  
        printf("%s
    ",recvBuf);  
        
        closesocket(sClient); //关闭套接字  
        WSACleanup();  
        //getchar();  没啥用,让你最后在显示终端可以输入一串字符,但是不能发送
        //return 0;  
    }  
      
    只此一生,与子白头
  • 相关阅读:
    iOS使用自签名证书实现HTTPS请求
    DB操作-用批处理执行Sql语句
    SSL通信-忽略证书认证错误
    oracle 19c awr 丢失 i/o信息
    this.$route.query刷新后类型改变
    wx.navigateTo在app.js中偶发性失效
    微信小程序new Date()转换日期格式时iphonex为NaN
    下载cnpm成功,cnpm -v却不识别
    element-ui的表单验证如何清除校验提示语
    5. 最长回文子串(动态规划算法)
  • 原文地址:https://www.cnblogs.com/Miss-Bueno/p/7435319.html
Copyright © 2011-2022 走看看