Socket 简单通信

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/weixin_40313634/article/details/80633155

功能:两个exe件实现信息交流。

优化方向:

1. 客户端断开连接后,服务器端应遵循四次握手,也要断开连接,重新绑定端口,继续监听等待下次连接。

2. 服务器或客户端均应可连续发送多条消息,而不是发送完毕后等待对方相应后才能继续发送。

3. 目前必须先启动服务器,再启动客户端。能否也可以先启动客户端,直接服务器启动后再建立连接,而不是直接断掉(当服务器未启动时)。

常用的 socket 接口函数:

1. int socket(int domain, int type, int protocol);

功能:根据指定的协议和类型,创建一个socket,并返回socket 的描述信息(类似创建文件后返回的文件指针)。

入参解释:

int domain :入参,指定socket 的协议族,常用的有 AF_INET(IPV4协议族)、AF_UNIX(UXIX 本地通信协议)等。 int type :指定socket 类型。常用的有 SOCK_STREAM(面向连接的,如发送邮件等)、SOCK_DGRAM(面向无连接的,如广播等)等等。

int protocol : 指定具体使用那个协议。常用的协议有,IPPROTO_TCP(TCP传输协议)、IPPTOTO_UDP(UDP传输协议)等等。

返回值: int socket :创建的socket 的描述符,唯一标识一个socket。

2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能:给创建的socket 绑定对应的协议和端口。

入参解释:

int sockfd :入参,socket 描述符。

sockaddr *addr :入参,指向要绑定到socket 上的协议信息。不同协议,指针结构不同。

struct sockaddr_in {     sa_family_t    sin_family; /* address family: AF_INET */     in_port_t      sin_port;   /* port in network byte order */     struct in_addr sin_addr;   /* internet address */ };   //  IPV4 指定的信息。  struct in_addr {     uint32_t       s_addr;     /* address in network byte order */ };

socklen_t addrlen : 入参,对应的sockaddr 的地址长度。

返回值: int bind :绑定成功与失败(SOCKET_ERROR )。

3. int listen(int sockfd, int backlog);

功能:服务器监听绑定的的端口,等待客户端连接。

入参解释:

int sockfd :入参,socket 描述符。

int backlog :入参,socket 可以排队的最大连接个数。

返回值:

int listen: 有无(SOCKET_ERROR )连接。

4. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能:客户机请求连接服务器。

入参解释:

int sockfd :入参,socket 描述符。

socketaddr *addr :入参,socket 的地址量。

socklen_t : 入参,socket 地址的长度。

返回值:

int conect: 客户机连接服务器成功与否。

5. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数解释:socket 描述符(入参),socket 的地址量(出参),socket 地址的长度(出参)。

6. int close(int fd);

功能:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

服务器端代码:

创建套接字 -> 绑定端口 -> 接受连接请求 -> 接受或发送数据 -> 断开连接。

#include "InitSock.h"    #include <stdio.h>    #include <iostream>   using namespace std;   CInitSock initSock;     // 初始化Winsock库       int main()    {        // 创建套节字:IPV4 协议族,面向无连接,TCP 协议。     SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);         if(sListen == INVALID_SOCKET)        {            printf("Failed socket() \n");            return 0;        }                // 填充套接字地址信息。     sockaddr_in sin;        sin.sin_family = AF_INET;        sin.sin_port = htons(4567);  //1024 ~ 49151:普通用户注册的端口号       sin.sin_addr.S_un.S_addr = INADDR_ANY;   // INADDR_ANY:本机IP,如果是多网卡机器,表示多个IP 的集合。             // 绑定套节字到本地主机地址        if(::bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)        {            printf("Failed bind() \n");            return 0;        }                // 监听端口,最多允许2个排队的socket。         if(::listen(sListen, 2) == SOCKET_ERROR)        {            printf("Failed listen() \n");            return 0;        }                // 循环接受客户的连接请求        sockaddr_in remoteAddr;         int nAddrLen = sizeof(remoteAddr);        SOCKET sClient = 0;        char szText[] = " TCP Server Demo! \r\n";        while(sClient==0)        {            // 接受一个新连接            //((SOCKADDR*)&remoteAddr)一个指向sockaddr_in结构的指针,用于获取对方地址           sClient = ::accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);            if(sClient == INVALID_SOCKET)            {                printf("Failed accept()");            }            else         {             printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));            }      }           while(TRUE)        {            // 向客户端发送数据            while( NULL != gets(szText) );                ::send(sClient, szText, strlen(szText), 0);                        // 从客户端接收数据            char buff[256] ;            int nRecv = 0;            while( ::recv(sClient, buff, 256, 0) > 0)            {                buff[nRecv] = '\0';                printf(" Client :%s\n", buff);            }         }           // 关闭同客户端的连接        ::closesocket(sClient);                    // 关闭监听套节字        ::closesocket(sListen);           return 0;    }   

客户端代码:

#include "InitSock.h"    #include <stdio.h>    #include <iostream>    using namespace std;   CInitSock initSock;     // 初始化Winsock库       int main()    {        // 创建套节字        SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);        if(s == INVALID_SOCKET)        {            printf(" Failed socket() \n");            return 0;        }                // 也可以在这里调用bind函数绑定一个本地地址        // 否则系统将会自动安排                // 填写远程地址信息        sockaddr_in servAddr;         servAddr.sin_family = AF_INET;        servAddr.sin_port = htons(4567);        // 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址        // 如果你的计算机没有联网,直接使用127.0.0.1即可        servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");                if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)        {            printf(" Failed connect() \n");            return 0;        }                char buff[256];        char szText[256] ;                while(TRUE)        {            //从服务器端接收数据            int nRecv = ::recv(s, buff, 256, 0);            if(nRecv > 0)            {                buff[nRecv] = '\0';                printf("接收到数据:%s\n", buff);            }               // 向服务器端发送数据               gets(szText) ;            szText[255] = '\0';            ::send(s, szText, strlen(szText), 0) ;                    }                // 关闭套节字        ::closesocket(s);        return 0;    }   

封装的InitSock.h

#include <winsock2.h>    #include <stdlib.h>     #include <conio.h>     #include <stdio.h>        #pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib       class CInitSock         {    public:        CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)        {            // 初始化WS2_32.dll            WSADATA wsaData;            WORD sockVersion = MAKEWORD(minorVer, majorVer);            if(::WSAStartup(sockVersion, &wsaData) != 0)            {                exit(0);            }        }        ~CInitSock()        {               ::WSACleanup();         }    };   

int socket(int domain, int type, int protocol);