C/C++ 创建Socket实现双工通信

Socket通信是一种用于在计算机网络中进行进程间通信的方法。它提供了一种简单而强大的通信机制,使得运行在不同计算机上的进程可以相互传递数据。

实现简单的Socket通信

服务端代码(Server)

这段代码是一个简单的TCP/IP服务器程序,主要实现以下功能:

  1. 初始化Winsock库:通过调用 WSAStartup 函数初始化 Winsock 库,这是在 Windows 系统上进行网络编程的必要步骤。

  2. 创建套接字:使用 socket 函数创建一个套接字,该套接字用于监听客户端连接请求。

  3. 绑定和侦听:将服务器套接字绑定到指定的端口,并使用 listen 函数将套接字设置为监听模式,准备接受客户端的连接请求。

  4. 接受连接:通过 accept 函数接受客户端的连接请求,并返回一个新的套接字(msgsock)用于与客户端进行通信。

  5. 接收数据:使用 recv 函数接收客户端发送的数据。

  6. 输出客户端信息:打印客户端的IP地址、端口号以及接收到的数据。

  7. 关闭套接字:关闭连接后的套接字,释放资源。

  8. 终止Winsock库使用:调用 WSACleanup 函数,终止对 Winsock 库的使用,并释放资源。

#include <stdio.h>  
#include <winsock2.h>  
#pragma comment(lib,"ws2_32.lib")
#define PORT 9999 

int main()
{
    WSADATA WSAData;
    SOCKET sock, msgsock;
    struct sockaddr_in ServerAddr;

    if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR)
    {
        ServerAddr.sin_family = AF_INET;
        ServerAddr.sin_port = htons(PORT);
        ServerAddr.sin_addr.s_addr = INADDR_ANY;

        sock = socket(AF_INET, SOCK_STREAM, 0);
        int BindRet = bind(sock, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
        int LinsRet = listen(sock, 10);
        printf("------------------------------------------------------------------------------------------\n");
        printf("绑定状态: %d\t 侦听状态: %d\t 绑定端口: %d\n", BindRet, LinsRet, ntohs(ServerAddr.sin_port));
        printf("------------------------------------------------------------------------------------------\n");
    }

    while (1)
    {
        char buf[1024];
        msgsock = accept(sock, (LPSOCKADDR)0, (int *)0);
        memset(buf, 0, sizeof(buf));
        recv(msgsock, buf, 1024, 0);
        printf("客户IP: %s\t 端口:%d\t 数据:%s\n", inet_ntoa(ServerAddr.sin_addr), htons(ServerAddr.sin_port),buf);
        if (!strcmp(buf, "Success"))
        {
            printf("客户端已就绪...\n");
        }
        closesocket(msgsock);
    }
    closesocket(sock);
    WSACleanup();
    return 0;
}

客户端代码(Client)

这段代码是一个简单的TCP/IP客户端程序,主要实现以下功能:

  1. 初始化Winsock库:通过调用 WSAStartup 函数初始化 Winsock 库,这是在 Windows 系统上进行网络编程的必要步骤。

  2. 创建套接字:使用 socket 函数创建一个套接字,该套接字将用于与服务器建立连接。

  3. 连接服务器:通过调用 connect 函数向服务器发起连接请求,并将套接字与服务器的IP地址和端口号绑定。

  4. 发送数据:如果成功连接服务器,客户端会向服务器发送一个包含 "Success" 字符串的数据。

  5. 关闭套接字:在完成数据发送后,客户端关闭套接字并释放资源。

  6. 终止Winsock库使用:调用 WSACleanup 函数,终止对 Winsock 库的使用,并释放资源。

  7. 等待一段时间后重复:程序在一个无限循环中,每隔 5 秒重新初始化 Winsock、连接服务器并发送数据。

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
#define IP "127.0.0.1"
#define PORT 9999

//#ifdef _MSC_VER
//#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
//#endif

int main()
{
    while (1)
    {
        WSADATA WSAData;
        SOCKET sock;
        struct sockaddr_in ClientAddr;

        if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR)
        {
            ClientAddr.sin_family = AF_INET;                //指Internet域
            ClientAddr.sin_port = htons(PORT);            //指定服务端所预留的端口
            ClientAddr.sin_addr.s_addr = inet_addr(IP);    //指定服务端所绑定的IP地址

            sock = socket(AF_INET, SOCK_STREAM, 0);
            int Ret = connect(sock, (LPSOCKADDR)&ClientAddr, sizeof(ClientAddr));
            if (Ret == 0)
            {
                char Status[] = "Success";
                int ServerRet = send(sock, Status, sizeof(Status), 0);
                if (ServerRet != 0)
                {
                    printf("发送给服务器Success状态成功..\n");
                }
            }
        }
        closesocket(sock);
        WSACleanup();
        Sleep(5000);
    }
    return 0;
}

实现Socket单工通信案例

服务端代码(Server)

这段代码是一个简单的TCP/IP服务器程序,用于接收客户端发送的数据并显示在服务器端。

主要实现步骤如下:

  1. 初始化Winsock库:通过调用 WSAStartup 函数初始化 Winsock 库,这是在 Windows 系统上进行网络编程的必要步骤。

  2. 创建套接字:使用 socket 函数创建一个流套接字,该套接字将用于监听客户端的连接请求。

  3. 绑定套接字:调用 bind 函数将本地地址绑定到所创建的套接字上,以在网络上标识该套接字。

  4. 监听连接请求:调用 listen 函数将套接字置入监听模式,并准备接受客户端的连接请求。

  5. 接受连接:使用 accept 函数接收客户端的连接请求,并返回一个新的套接字用于与该客户端进行通信。

  6. 接收数据:调用 recv 函数从客户端接收数据,并将数据存储在 buf 缓冲区中。

  7. 处理数据:在这段代码中,服务器会检查接收到的数据是否为 "exit" 命令。如果是 "exit" 命令,服务器会退出循环,结束程序;否则,服务器会将接收到的数据显示在控制台上,并继续监听其他客户端连接请求。

  8. 关闭套接字:在处理完数据后,关闭与客户端通信的套接字,并释放资源。

  9. 终止Winsock库使用:调用 WSACleanup 函数,终止对 Winsock 库的使用,并释放资源。

#include <stdio.h>  
#include <winsock2.h>  
#pragma comment(lib,"ws2_32.lib")    //把ws2_32.lib加到Link页的连接库  
#define PORT 15001                    //通信的端口(指服务器端)
#define ERROR 0  
#define BUFFER_SIZE 1024            //注意:此Server端数据接收缓冲区 >= Client端数据发送缓冲区 ,否则造成缓冲区溢出
/*
    服务端原理:
        1、服务器进程创建套接字
        2、将本地地址绑定到所创建的套接字上,以三元组{<通信协议>,<IP地址>,<端口号>}在网络上标识该套接字
        3、将套接字置入监听模式,并准备接受连接请求
*/
int main()  
{  
    WSADATA WSAData;  
    if(WSAStartup(MAKEWORD(2,0),&WSAData)==SOCKET_ERROR)  //启动winsock ,WSAStartup()函数对Winsock DLL进行初始化
    {  
        printf("Socket initialize fail!\n");  
        exit(1);  
    }  
    SOCKET sock;                                        //服务进程创建套接字句柄(用于监听)
    if((sock=socket(AF_INET,SOCK_STREAM,0))==ERROR)        //调用socket()函数创建一个流套接字,参数(网络地址类型,套接字类型,网络协议)
    { 
        printf("Socket create!\n");  
        WSACleanup();  
        exit(1);  
    }  
    struct sockaddr_in ServerAddr;            //sockaddr_in结构用来标识TCP/IP协议下的地址,可强制转换为sockaddr结构
    ServerAddr.sin_family=AF_INET;            //sin_family字段必须设为AF_INET,表示该Socket处于Internet域
    ServerAddr.sin_port=htons(PORT);        //sin_port字段用于指定服务端口,注意避免冲突
    ServerAddr.sin_addr.s_addr=INADDR_ANY;  //sin_addr字段用于把一个IP地址保存为一个4字节的数,无符号长整型,根据不同用法还可表示本地或远程IP地址
    if(bind(sock,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr))==SOCKET_ERROR)  //调用bind()函数将本地地址绑定到所创建的套接字上,以在网络上标识该套接字
    {   
        printf("Bind fail!\n");  
        closesocket(sock);  
        WSACleanup();  
        exit(1);  
    }  
    printf("Server Socket Port:%d\n",ntohs(ServerAddr.sin_port));  
    if(listen(sock,10)==SOCKET_ERROR)        //调用listen()函数将套接字置入监听模式并准备接受连接请求,参数(已捆绑未连接的套接字描述字,正在等待连接的最大队列长度)
    { 
        printf("Listen fail!\n");  
        closesocket(sock);  
        WSACleanup();  
        exit(1);  
    }  

    SOCKET msgsock;            //创建一个新的套接字(用于接收accept函数的返回值,即表示已经接受的那个客户端的连接,进而接收Client发来的数据)
    char buf[BUFFER_SIZE];  //数据接收缓冲区
    while(1)  
    {  
        if((msgsock=accept(sock,(LPSOCKADDR)0,(int *)0))==INVALID_SOCKET)  //进入监听状态后,调用accept()函数接收客户端的连接请求,并把连接传给msgsock套接字,原sock套接字继续监听其他客户机连接请求
        {  
            printf("Accept fail!\n");  
            continue;  
        }  
        memset(buf,0,sizeof(buf));                                            //初始化数据接收缓冲区
        recv(msgsock,buf,BUFFER_SIZE,0);                                    //接收客户端发送过来的数据  
        if(buf[0]=='e' && buf[1]=='x' && buf[2]=='i' && buf[3]=='t')        //"exit"命令,退出程序
        {  
            printf("The End.\n");  
            break;  
        }
        printf("C:\\Socket\\Server>%s",buf);  
        closesocket(msgsock);  
    }  

    closesocket(sock); //关闭套接字  
    WSACleanup();       //终止对Winsock DLL的使用,并释放资源
    return 0;
}   

客户端代码(Client)

这段代码是一个简单的TCP/IP客户端程序,用于向服务器发送消息。

主要实现步骤如下:

  1. 接收用户输入:通过 getchar 函数接收用户输入的消息,并将其存储在 buf 缓冲区中。

  2. 初始化Winsock库:通过调用 WSAStartup 函数初始化 Winsock 库,这是在 Windows 系统上进行网络编程的必要步骤。

  3. 创建套接字:使用 socket 函数创建一个流套接字,该套接字将用于与服务器进行通信。

  4. 连接服务器:调用 connect 函数向服务器进程发出连接请求,将客户端与服务器建立连接。

  5. 发送数据:调用 send 函数向服务器发送数据,将用户输入的消息发送给服务器。

  6. 关闭套接字:在完成数据发送后,关闭与服务器的连接套接字,并释放资源。

  7. 终止Winsock库使用:调用 WSACleanup 函数,终止对 Winsock 库的使用,并释放资源。

#include <winsock2.h>  
#include <stdio.h>  
#pragma comment(lib,"ws2_32.lib")    //把ws2_32.lib加到Link页的连接库  
//#define IP "172.18.68.243"            //在两台计算机上测试,IP为Server端的IP地址  
#define IP "127.0.0.1"                //在一台计算机上测试,IP为本地回送地址
#define PORT 15001                    //注意:客户端设置通信的端口 = 服务端的端口
#define BUFFER_SIZE 1024            //数据发送缓冲区大小

int main()  
{  
    char buf[BUFFER_SIZE];                                //buf数组存放客户端发送的消息  
    int inputLen;                                        //用于输入字符自增变量
    while(1)  
    {  
        printf("C:\\Socket\\Client>");  
        inputLen=0;  
        memset(buf,0,sizeof(buf));  
        while((buf[inputLen++]=getchar())!='\n')        //输入以回车键为结束标识
        {
            ;
        }
        if(buf[0]=='e' && buf[1]=='x' && buf[2]=='i' && buf[3]=='t')  
        {  
            printf("The End.\n");  
            break;   
        }  

        WSADATA WSAData;  
        if(WSAStartup(MAKEWORD(2,0),&WSAData)==SOCKET_ERROR)  //WSAStartup()函数对Winsock DLL进行初始化
        {  
            printf("Socket initialize fail!\n");  
            continue;  
        }  
        SOCKET sock;                                            //客户端进程创建套接字
        if((sock=socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR)  //创建流套接字(与服务端保持一致)
        {  
            printf("Socket create fail!\n");  
            WSACleanup();  
            continue;  
        }  

        struct sockaddr_in ClientAddr;                //sockaddr_in结构用来标识TCP/IP协议下的地址,可强制转换为sockaddr结构
        ClientAddr.sin_family=AF_INET;                //指Internet域
        ClientAddr.sin_port=htons(PORT);            //指定服务端所预留的端口
        ClientAddr.sin_addr.s_addr=inet_addr(IP);    //指定服务端所绑定的IP地址
        if(connect(sock,(LPSOCKADDR)&ClientAddr,sizeof(ClientAddr))==SOCKET_ERROR)  //调用connect()函数,向服务器进程发出连接请求  
        { 
            printf("Connect fail!\n");  
            closesocket(sock);  
            WSACleanup();  
            continue;  
        }  
        send(sock,buf,BUFFER_SIZE,0);                 //向服务器发送数据 
        closesocket(sock);                             //关闭套接字
        WSACleanup();                                //终止对Winsock DLL的使用,并释放资源,以备下一次使用
    }  
    return 0;
}  

实现Socket双工通信案例

服务端代码(Server)

主要实现步骤如下:

  1. 初始化套接字动态库:通过调用 WSAStartup 函数初始化 Winsock 库,这是在 Windows 系统上进行网络编程的必要步骤。

  2. 创建服务端套接字:使用 socket 函数创建一个TCP套接字,用于监听客户端连接。

  3. 绑定套接字:调用 bind 函数将套接字与指定的本地地址(IP地址和端口号)绑定在一起,以便客户端能够连接到服务器。

  4. 监听套接字:调用 listen 函数将套接字置于监听状态,等待客户端连接请求。

  5. 接受客户端连接:调用 accept 函数接受客户端的连接请求,返回一个新的套接字 sClient,用于与客户端进行通信。

  6. 创建两个线程:使用 CreateThread 函数创建两个线程,分别用于接收客户端发来的消息和向客户端发送消息。

  7. 等待线程结束:通过 WaitForSingleObject 函数等待两个线程执行结束。

  8. 关闭套接字和释放资源:在完成通信后,关闭与客户端的连接套接字 sClient,关闭服务端套接字 sServer,并调用 WSACleanup 函数,终止对 Winsock 库的使用并释放资源。

#include <stdio.h>
#include <Winsock2.h> //Socket的函数调用 
#include <windows.h>

#define BUF_SIZE 6400  //  缓冲区大小

#pragma comment (lib, "ws2_32")     // 使用WINSOCK2.H时,则需要库文件WS2_32.LIB

DWORD WINAPI Rcv(LPVOID lpParam)
{
    SOCKET sClient = *(SOCKET*)lpParam;
    int retVal;
    char bufRecv[BUF_SIZE];
    memset(bufRecv, 0, sizeof(bufRecv));
    while (1)
    {
        retVal = recv(sClient, bufRecv, BUF_SIZE, 0);
        if (retVal == SOCKET_ERROR) {
            printf("recive faild!\n");
            break;
        }
        else {
            printf("收到客户端消息:%s\n", bufRecv);
        }
    }
    return 0;
}

DWORD WINAPI Snd(LPVOID lpParam)
{
    SOCKET sClient = *(SOCKET*)lpParam;
    int retVal;
    char bufSend[BUF_SIZE];
    memset(bufSend, 0, sizeof(bufSend));
    while (1)
    {
        gets(bufSend);
        retVal = send(sClient, bufSend, strlen(bufSend) + sizeof(char), 0);
        if (retVal == SOCKET_ERROR) {
            printf("send faild!\n");
            break;
        }
    }
    return 0;
}


int main(int argc, char* argv[])
{
    // 初始化套接字动态库
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("winsock load faild!\n");
        return 1;
    }

    //  创建服务段套接字
    SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sServer == INVALID_SOCKET) {
        printf("socket faild!\n");
        WSACleanup();
        return -1;
    }

    //  服务端地址
    sockaddr_in addrServ;

    addrServ.sin_family = AF_INET;
    addrServ.sin_port = htons(9999);
    addrServ.sin_addr.s_addr = htonl(INADDR_ANY);

    //  绑定套接字
    if (bind(sServer, (const struct sockaddr*)&addrServ, sizeof(addrServ)) == SOCKET_ERROR) {
        printf("bind faild!\n");
        closesocket(sServer);
        WSACleanup();
        return -1;
    }

    printf("Server is On IP:[%s],port:[%d]\n", inet_ntoa(addrServ.sin_addr), ntohs(addrServ.sin_port));

    //  监听套接字  数字表示最多能监听客户个数
    if (listen(sServer, 5) == SOCKET_ERROR) {
        printf("listen faild!\n");
        closesocket(sServer);
        WSACleanup();
        return -1;
    }

    SOCKET sClient; //  客户端套接字

    sockaddr_in addrClient;
    int addrClientLen = sizeof(addrClient);


    sClient = accept(sServer, (sockaddr FAR*)&addrClient, &addrClientLen);
    if (sClient == INVALID_SOCKET) {
        printf("accept faild!\n");
        closesocket(sServer);
        WSACleanup();
        return -1;
    }
    printf("accepted client IP:[%s],port:[%d]\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));

    HANDLE hThread1, hThread2;
    DWORD dwThreadId1, dwThreadId2;

    hThread1 = ::CreateThread(NULL, NULL, Snd, (LPVOID*)&sClient, 0, &dwThreadId1);
    hThread2 = ::CreateThread(NULL, NULL, Rcv, (LPVOID*)&sClient, 0, &dwThreadId2);

    ::WaitForSingleObject(hThread1, INFINITE);
    ::WaitForSingleObject(hThread2, INFINITE);
    ::CloseHandle(hThread1);
    ::CloseHandle(hThread2);

    closesocket(sClient);
    WSACleanup(); // 资源释放

    return 0;
}

客户端代码(Client)

这段代码是一个简单的多线程TCP/IP客户端程序,用于与服务器建立连接并实现双向通信。

主要实现步骤如下:

  1. 初始化套接字动态库:通过调用 WSAStartup 函数初始化 Winsock 库,这是在 Windows 系统上进行网络编程的必要步骤。

  2. 创建客户端套接字:使用 socket 函数创建一个TCP套接字,用于与服务器建立连接。

  3. 设置服务器地址:使用 SOCKADDR_IN 结构来设置服务器的地址信息,包括服务器的 IP 地址和端口号。

  4. 连接服务器:调用 connect 函数将客户端套接字与服务器建立连接。

  5. 创建两个线程:使用 CreateThread 函数创建两个线程,分别用于从标准输入读取用户输入,并发送到服务器,以及从服务器接收数据并打印到标准输出。

  6. 等待线程结束:通过 WaitForSingleObject 函数等待两个线程执行结束。

  7. 关闭套接字和释放资源:在完成通信后,关闭客户端套接字 sHost,并调用 WSACleanup 函数,终止对 Winsock 库的使用并释放资源。

#include <stdio.h>
#include <Winsock2.h> //Socket的函数调用 
#include <windows.h>

#define BUF_SIZE 6400

#pragma comment (lib, "ws2_32")     // 使用WINSOCK2.H时,则需要库文件WS2_32.LIB

DWORD WINAPI Rcv(LPVOID lpParam)
{
    SOCKET sHost = *(SOCKET*)lpParam;
    int retVal;
    char bufRecv[BUF_SIZE];
    memset(bufRecv, 0, sizeof(bufRecv));
    while (1)
    {
        retVal = recv(sHost, bufRecv, BUF_SIZE, 0);
        if (retVal == SOCKET_ERROR) {
            printf("recive faild!\n");
            break;
        }
        else {
            printf("收到服务器消息:%s\n", bufRecv);
        }
    }
    return 0;
}

DWORD WINAPI Snd(LPVOID lpParam)
{
    SOCKET sHost = *(SOCKET*)lpParam;
    int retVal;
    char bufSend[BUF_SIZE];
    memset(bufSend, 0, sizeof(bufSend));
    while (1)
    {
        gets(bufSend);
        retVal = send(sHost, bufSend, strlen(bufSend) + sizeof(char), 0);
        if (retVal == SOCKET_ERROR) {
            printf("send faild!\n");
            break;
        }
    }
    return 0;
}

int main(int argc, char* argv[])
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("Winsock load faild!\n");
        return 1;
    }

    //  服务器套接字
    SOCKET sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sHost == INVALID_SOCKET) {
        printf("socket faild!\n");
        WSACleanup();
        return -1;
    }

    SOCKADDR_IN servAddr;
    servAddr.sin_family = AF_INET;
    //  注意   当把客户端程序发到别人的电脑时 此处IP需改为服务器所在电脑的IP
    servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    servAddr.sin_port = htons(9999);

    //  连接服务器
    if (connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) {
        printf("connect faild!\n");
        closesocket(sHost);
        WSACleanup();
        return -1;
    }
    printf("连接到服务器 IP:[%s],port:[%d]\n", inet_ntoa(servAddr.sin_addr), ntohs(servAddr.sin_port));

    HANDLE hThread1, hThread2;
    DWORD dwThreadId1, dwThreadId2;

    hThread1 = ::CreateThread(NULL, NULL, Snd, (LPVOID)&sHost, 0, &dwThreadId1);
    hThread2 = ::CreateThread(NULL, NULL, Rcv, (LPVOID)&sHost, 0, &dwThreadId2);

    ::WaitForSingleObject(hThread1, INFINITE);
    ::WaitForSingleObject(hThread2, INFINITE);
    ::CloseHandle(hThread1);
    ::CloseHandle(hThread2);


    closesocket(sHost);
    WSACleanup();
    return 0;
}
  • 2
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微软技术分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值