C/C++ socket套接字详解(Windows)

一、编译环境

本篇博客是在windows系统下的CodeBlocks环境下编写而成的,Linux系统以及其他编译环境暂不适用

关于如何CodeBlocks如何安装和配置/,可以参考链接(转自萤火虫塔莉):CodeBlocks的安装以及编译器的配置

常见问题:

编译时不能识别socket,需要手动导入lib库。具体方法链接:https://blog.csdn.net/buaa1214wwj/article/details/52033868

如果导这两个lib库仍然不能识别,出现以下情况,重新建立C++工程。


二、socket用法简介

(1)socket概念
  • socket翻译为套接字,它是计算机之间通信的一种约定或一种方式,通过socket这种约定,一台计算机可以接受其他计算机的数据,也可以向其他计算机发送数据。
  • 学习socket,也就是学习计算机如何通信,并且编写出实用的程序。
(2)Windows程序示例

服务器演示代码Server.cpp

#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll


int main(){
    //初始化 DLL
    WSADATA wsaData;
    WSAStartup( MAKEWORD(2, 2), &wsaData);


    //创建套接字
    SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);


    //绑定套接字
    sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
    sockAddr.sin_family = PF_INET;  //使用IPv4地址
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    sockAddr.sin_port = htons(1234);  //端口
    bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));


    //进入监听状态
    listen(servSock, 20);


    //接收客户端请求
    SOCKADDR clntAddr;
    int nSize = sizeof(SOCKADDR);
    SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);


    //向客户端发送数据
    char *str = "Hello World!";
    send(clntSock, str, strlen(str)+sizeof(char), NULL);


    //关闭套接字
    closesocket(clntSock);
    closesocket(servSock);


    //终止 DLL 的使用
    WSACleanup();


    return 0;
}

客户端演示代码Client.cpp

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll
int main(){
    //初始化DLL
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    //创建套接字
    SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    //向服务器发起请求
    sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
    sockAddr.sin_family = PF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockAddr.sin_port = htons(1234);
    connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
    //接收服务器传回的数据
    char szBuffer[MAXBYTE] = {0};
    recv(sock, szBuffer, MAXBYTE, NULL);
    //输出接收到的数据
    printf("Message form server: %s\n", szBuffer);
    //关闭套接字
    closesocket(sock);
    //终止使用 DLL
    WSACleanup();
    system("pause");
    return 0;
}
(3)程序解释

1. #pragma comment (lib, "ws2_32.lib") :Windows下的socket程序依赖Winsock.dll或者ws2_32.dll,所以必须提前加载
2. WSAStartup()函数对dll初始化,以指明Winsock的版本号,原型:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

    1)wVersionRequested 为 WinSock 规范的版本号,一般采用2.2版本,类型为 WORD,等价于 unsigned short,是一个整数,所以需要用 MAKEWORD() 宏函数对版本号进行转换。
    2)lpWSAData 为指向 WSAData 结构体的指针。

3.socket() 函数创建套接字,原型:(Windows 不把套接字作为普通文件对待,而是返回 SOCKET 类型的句柄)

SOCKET socket(int af, int type, int protocol);

    1)af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET(IPv4) 和 AF_INET6(IPv6) 。

    2)type 为数据传输方式,常用的有 SOCK_STREAM (面向连接的数据传输方式)和 SOCK_DGRAM(无连接的数据传输方式)。

    3)protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。

4. bind() 函数服务器端将套接字与特定的IP地址和端口绑定;connect() 函数客户端用来建立连接

   1) bind()原型:

int bind(SOCKET sock, const struct sockaddr *addr, int addrlen); 

    2)connet()原型:

int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);

sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出

sockaddr_in 结构体,成员变量如下:

struct sockaddr_in{
    sa_family_t     sin_family;   //地址族(Address Family),也就是地址类型
    uint16_t        sin_port;     //16位的端口号
    struct in_addr  sin_addr;     //32位IP地址
    char            sin_zero[8];  //不使用,一般用0填充
};

5.listen()函数让套接字进入被动监听状态,它的原型为:

int listen(SOCKET sock, int backlog);

sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。

6.accept() 函数当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求,它的原型为:

SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。

特别说明:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。

7. send() 函数发送数据,recv() 函数接受数据

    1)send()原型:

int send(SOCKET sock, const char *buf, int len, int flags);

     2)recv()原型:

int recv(SOCKET sock, char *buf, int len, int flags);

sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数,flags 为发送数据时的选项。

8.closesocket()函数关闭套接字,原型为:

void closesocket(SOCKET Sock);

9.WSACleanup()函数终止 DLL 的使用。


   

  • 13
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值