面向连接的Socket编程

面向连接的Socket通信流程

服务器端:

  1. 创建流式套接字,返回套接字号s
  2. 将套接字s绑定到一个已知地址,通常为本地IP地址
  3. 将套接字s设置为监听模式,准备接收各个客户机的请求
  4. 建立连接,得到新的套接字ns
  5. 在套接字上读写数据,直到结束
  6. 关闭套接字ns
  7. 关闭套接字s

客户端:

  1. 创建流式套接字,返回套接字号s
  2. 将套接字s与远程主机连接
  3. 在套接字上读写数据,直到结束
  4. 关闭套接字,结束会话
    面向连接的Socket编程_第1张图片

Socket编程相关函数

  1. socket()

    用于创建与指定的服务提供者绑定套接字,如果成功则返回新的socket句柄,否则返回INVALID_SOCKET.

    SOCKET socket(
      int af,    /*指定协议的地址家族,通常使用AF_INET*/
      int type,    /*指定套接字类型*/
      int protocol    /*套接字的协议*/
    );
    套接字类型 说明
    SOCKET_STREAM 提供顺序、可靠、双向和面向连接的字节数据传输机制,使用TCP
    SOCKET_DGRAM 支持无连接的数据报,使用UDP
    SOCKET_RAM 原始套接字,用于接收本机网卡上的数据帧或数据包
  2. bind()

    将本地地址与socket绑定在一起,如果未发生错误,则返回0,否则返回SOCKET_ERROR.

    int bind(
        SOCKET s,    /*标识一个未绑定的Socket的描述符*/
        const struct sockaddr FAR * name,    /*绑定到Socket的sockaddr结构体地址*/
        int namelen    /*参数name的长度*/
    );

    如果应用程序不关心分配给它的本地地址,则可以把地址设置未ADDR_ANY.

  3. listen()
    将套接字设置为监听接入连接的状态,如果未发生错误,则返回0,否则返回SOCKET_ERROR。可以调用WSAGetLastError()函数获取错误代码

    int listen(
        SOCKET s,    /*指定一个已经绑定(bind)但尚未连接的套件字*/
        int backlog    /*指定等待连接队列的最大长度*/
    );
  4. accept()
    服务器端调用listen()函数监听接入连接后,可以调用accept()函数来等待接受连接请求。如果函数调用成功,则返回一个新建的Socket句柄,该Socket用于实现服务器和客户端之间的通信。如果调用失败,则返回INVALID_SOCKET

    SOCKET accept(
        SOCKET s,    /*设置为监听状态的Socket*/
        struct sockaddr FAR * addr,    /*输出参数,用于接收接入地址信息,如果不关注接入地址,可以设置为NULL,可选*/
        int FAR * addrlen    /*输出参数,指定接入地址的长度,可选*/
    );
  5. recv()
    从已连接的Socket中接收数据。如果函数调用成功,则返回接收数据的字节数;如果连接已经关闭,则返回0;否则返回SOCKET_ERROC.

    int recv(
        SOCKET s,   /*已连接的Socket*/
        char FAR * buf,    /*用于接收数据的缓存区*/
        int len,    /**缓冲区的长度/
        int flags    /*用于影响函数的行为*/
    );
    参数flag的可选值 说明
    MSG_PEEK 数据将复制到缓冲区buf中,但并不从输入队列中移除这些数据
    MSG_OOB 处理带外数据
  6. send()
    在已建立连接的Socket上发送数据。如果函数调用成功,则返回发送数据的字节数,可以小于参数len中指定的长度;如果出现错误,则返回SOCKET_ERROC.

    int send(
        SOCKET s,   /*已连接的Socket*/
        const char FAR * buf,    /*要发送数据的缓存区*/
        int len,    /*buf缓冲区的长度*/
        int flags    /*用于影响函数的行为*/
    );
    参数flag的可选值 说明
    0 指定该函数没有特殊行为
    MSG_DONTROUTE 指定数据不选择路由
    MSG_OOB 从带外发送数据
  7. closesocket()
    关闭一个Socket,释放其所占用的资源。如果没有错误则返回0;否则返回SOCKET_ERROC.

    int closesocket(
        SOCKET s    /*要关闭的Socket*/
    );
  8. shutdown()
    禁止在指定的Socket上发送和接收数据。如果没有错误则返回0;否则返回SOCKET_ERROC.

    int shutdown(
        SOCKET s,   /*要关闭的Socket*/
        int how    /*指定禁用的操作*/
    );
    参数how的选项 说明
    SD_RECEIVE 不允许调用recv()接收数据
    SD_SEND 不允许调用send()发送数据
    SD_BOTH 不允许发送接收数据
  9. connect()
    用于建立到Socket的连接,该Socket必须处于监听状态。如果没有错误则返回0;否则返回SOCKET_ERROC.

    int connect(
        SOCKET s,    /*未连接的Socket句柄*/
        const struct sockaddr FAR * name,    /*指定要连接的Socket的名称*/
        int namelen    /*指定Socket名的长度*/
    );

TCP Socket编程实例

TCP服务器端:

#include 
#include 
#include  
#pragma comment(lib,"WS2_32.lib")

#define BUF_SIZE 64 //服务器接收数据缓冲区大小

int _tmain(int argc,_TCHAR* argv[])
{
    //初始化WindowsSocket
    WSADATA wsd;
    SOCKET sServer;
    SOCKET sClient;
    int retVal;     //调用各种socket函数的返回值
    char buf[BUF_SIZE];

    //初始化Socket动态库
    if(WSAStartup(MAKEWORD(2,2),&wsd)!=0){
        printf("WSASTARTUP Failed!\n");
        return 1;
    }

    //创建用于监听的socket
    sServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (INVALID_SOCKET == sServer)
    {
        printf("socket failed!\n");
        WSACleanup();
        return -1;
    }

    //设置服务器Socket地址
    SOCKADDR_IN addrServ;
    addrServ.sin_family = AF_INET;      //地址家族,TCP
    addrServ.sin_port = htons(9990);    //端口
    addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);      //地址

    //绑定socket server到本地
    retVal = bind(sServer,(const struct sockaddr*)&addrServ,sizeof(SOCKADDR_IN));
    if (SOCKET_ERROR == retVal)
    {
        printf("bind failed!\n");
        closesocket(sServer);
        WSACleanup();
        return -1;
    }

    //在socketscrver上进行监听
    retVal = listen(sServer,1                                                 );
    if (SOCKET_ERROR == retVal)
    {
        printf("listen failed!\n");
        closesocket(sServer);
        WSACleanup();
        return -1;
    }

    //接收请求
    printf("TCP server start...\n");
    sockaddr_in addrClient;     //客户端地址
    int addrClientLen = sizeof(addrClient);
    sClient = accept(sServer,(sockaddr FAR*)&addrClient,&addrClientLen);
    if (INVALID_SOCKET == sClient)
    {
        printf("accept failed\n");
        closesocket(sServer);
        WSACleanup();
        return -1;
    }

    while(true)
    {
        ZeroMemory(buf,BUF_SIZE);
        retVal = recv(sClient,buf,BUFSIZ,0);
        if (SOCKET_ERROR == retVal)
        {
            printf("recv failed\n");
            closesocket(sServer);
            closesocket(sClient);
            WSACleanup();
            return -1;
        }
        //获取当前系统时间
        SYSTEMTIME st;
        GetLocalTime(&st);
        char sDataTime[30];
        sprintf(sDataTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
        printf("%s,Recv From Cliet [%s:%d] :%s\n",sDataTime,inet_ntoa(addrClient.sin_addr),addrClient.sin_port,buf);
        //如果客户端发送字符串为quit,则退出
        if(strcmp(buf,"quit")==0){
            retVal = send(sClient,"quit",strlen("quit"),0);
            break;
        }
        else{
            char msg[BUF_SIZE];
            sprintf(msg,"message received - %s",buf);
            retVal = send(sClient,msg,sizeof(msg),0);
            if(SOCKET_ERROR == retVal){
                printf("send failed\n");
                closesocket(sServer);
                closesocket(sClient);
                WSACleanup();
                return -1;
            }
        }
    }
    //释放socket
    closesocket(sServer);
    closesocket(sClient);
    WSACleanup();
    system("pause");
}

TCP客户端:

#include 
#include 
#include 
#include 

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

#define BUF_SIZE 64

int _tmain(int argc,_TCHAR* argv[])
{
    WSADATA wsd;
    SOCKET sHost;
    SOCKADDR_IN servAddr;
    char buf[BUF_SIZE];
    int retVal;

    //初始化socket
    if (WSAStartup(MAKEWORD(2,2),&wsd)!=0)
    {
        printf("WSASTARTUP failed!\n");
        return -1;
    }

    //创建socket
    sHost = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(INVALID_SOCKET == sHost)
    {
        printf("socket failed\n");
        WSACleanup();
        return -1;

    }
    //设置服务器socket地址
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    servAddr.sin_port = htons(9990);
    int sServerAddLen = sizeof(servAddr);
    //连接服务器
    retVal = connect(sHost,(LPSOCKADDR)&servAddr,sizeof(servAddr));
    if(SOCKET_ERROR == retVal)
    {
        printf("connect failed\n");
        closesocket(sHost);
        WSACleanup();
        return -1;
    }
    while(true)
    {
        printf("Input:");
        std::string str;
        std::getline(std::cin,str);
        ZeroMemory(buf,BUF_SIZE);
        strcpy(buf,str.c_str());
        retVal = send(sHost,buf,strlen(buf),0);
        if(SOCKET_ERROR == retVal)
        {
            printf("send failed\n");
            closesocket(sHost);
            WSACleanup();
            return -1;
        }
        retVal = recv(sHost,buf,sizeof(buf)+1,0);
        printf("Recv:%s\n",buf);
        if (strcpy(buf,"quit")==0)
        {
            printf("quit!\n");
            break;
        }
    }
    closesocket(sHost);
    WSACleanup();
    system("pause");
    return 0;
}

你可能感兴趣的:(windows网络编程)