Posts /

Socket通信

15 Aug 2020

Socket通信


参考:

Linux之本地Socket通信

Linux Socket过程详细解释(包括三次握手建立连接,四次握手断开连接)

Linux socket 本地进程间通信

最近笔试遇到一个选择题,说在Linux进行Socket通信的时候那个阶段不会阻塞。由于没有接触过这方面的内容,通过查找一些资料进行学习。

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。它把复杂的TCP/IP协议隐藏在Socket接口后,对用户来说一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

Socket通信流程

服务器端需要创建Socket,绑定地址,进行监听。

客户端需要创建Socket,建立连接(指定地址),然后进行数据交互。

流程图如下:

1597478727294

C++的连接用send发送数据,recv接收数据。

Socket 服务端例子

// Server.cpp
#include <stdio.h>
#include <winsock2.h>

int main()
{
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA wsaData;
    // 初始化WSA
    if (WSAStartup(sockVersion, &wsaData) != 0)
    {
        return 0;
    }
    /**
        AF_INET  Arpa(TCP/IP) 网络通信协议(IPV4)
        AF_INET6  IPV6
        AF_UNIX  UNIX 域协议(文件系统套接字)(或称AF_LOCAL   ,Unix域socket)
        AF_ISO    ISO标准协议
        AF_NS    施乐网络体统协议
        AF_IPX  Novell IPX 协议
        AF_APPLETALK   Appletalk DDS
        AF_ROUTE 路由套接字
        AF_KEY 密钥套接字
    */
    //创建套接字
    SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (slisten == INVALID_SOCKET)
    {
        printf("socket error!");
        return -1;
    }
    // 绑定IP和端口
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;

    if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf("bind error!");
    }
    //开始监听
    if (listen(slisten, 5) == SOCKET_ERROR)
    {
        printf("listen error!");
    }
    // 循环接受数据
    SOCKET sClient;
    sockaddr_in remoteAddr;
    int nAddrlen = sizeof(remoteAddr);
    char revData[255];
    while (true)
    {
        printf("\nWaiting for connect...\n");
        sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
        if (sClient == INVALID_SOCKET)
        {
            printf("accept error !");
            continue;
        }
        printf("\nACCEPT an connection: %s \r\n", inet_ntoa(remoteAddr.sin_addr));

        //接收数据
        int ret = recv(sClient, revData, 255, 0);
        if (ret > 0)
        {
            revData[ret] = 0x00;
            printf(revData);
        }
        //发送数据
        const char *sendData = "\nHello,TCP clinet!\n";
        send(sClient, sendData, strlen(sendData), 0);
        closesocket(sClient);
    }
    closesocket(slisten);
    WSACleanup();
    return 0;
}

Socket 客户端连接例子

// Clinet.cpp
#include <WINSOCK2.H>
#include <STDIO.H>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
#pragma comment(lib, "ws2_32.lib")

int main()
{
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA data;
    if (WSAStartup(sockVersion, &data) != 0)
    {
        return 0;
    }
    while (true)
    {
        // 创建SOCKET
        SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (sclient == INVALID_SOCKET)
        {
            printf("invalid socket!");
            return 0;
        }
        sockaddr_in serAddr;
        serAddr.sin_family = AF_INET;
        serAddr.sin_port = htons(8888);
        serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
        if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
        { //连接失败
            printf("connect error !");
            closesocket(sclient);
            return 0;
        }

        string data;
        std::cin >> data;
        const char *sendData;
        sendData = data.c_str(); //string转const char*
        //char * sendData = "你好,TCP服务端,我是客户端\n";
        send(sclient, sendData, strlen(sendData), 0);
        //send()用来将数据由指定的socket传给对方主机
        //int send(int s, const void * msg, int len, unsigned int flags)
        //s为已建立好连接的socket,msg指向数据内容,len则为数据长度,参数flags一般设0
        //成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error

        char recData[255];
        int ret = recv(sclient, recData, 255, 0);
        if (ret > 0)
        {
            recData[ret] = 0x00;
            printf(recData);
        }
        closesocket(sclient);
    }

    WSACleanup();
    return 0;
}