添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 编译环境:VS Code + gcc,环境搭建可以参考 这里
  • 本系列文章参考 尹圣雨 著的《TCP/IP网络编程》。

所有学习都要在开始前认识到其必要性 !这是我经常挂在嘴边的一句话。从语言的基本语法到系统函数,若无法回答“这到底有何必要?”学习过程将变得枯燥无味,而且很容易遗忘。
— 尹圣雨

SOCKET 编程头文件和库

使用 Windows Socket 编程,需要 winsock2.h 头文件和 ws2_32.lib 库。
对于 gcc 编译器,加载 ws2_32.lib 库需要在编译参数中添加参数: -lwsock32 。否则 gcc 会因为没有找到库而报错(编译的时候出现
undefined reference to `__imp_WSAStartup’)。
具体添加方法为:

  1. 在工程文件夹下打开 .vscode 文件夹
  2. 打开 tasks.json 文件,在 args 字段中添加新的参数 -lwsock32 ,如下图所示。
    请添加图片描述

Windows SOCKET 初始化

以下是 Windows SOCKET 编程固定格式。

#include <winsock2.h>
int main(int argc, char *argv[])
    WSADATA wsaData;
    // ...
    if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    	// ...
    	exit(1);
	// ...
    WSACleanup();
    return 0;

首先必须调用 WSAStartup 函数,设置程序中用到的 Windows SOCKET 版本,并初始化相应版本的库。
WSAStartup 函数原型为:

/*成功返回 0 ,失败返回非零错误码*/
int  WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

Windows SOCKET 存在多个版本,参数 wVersionRequested 指明使用哪个版本。版本占用 2 字节,高 8 位为副版本,低 8 位为主版本,比如 V1.2 版本,则传递 0x0201。一般会使用 MAKEWORD 宏来构建版本信息:

WORD ver = MAKEWORD(1, 2);			// V1.2 版本, ver = 0x0201

第二个参数 lpWSAData 用于保存库信息。

其次,当程序结束之前,需要调用 WSACleanup 注销 SOCKET 库。

Windows SOCKET 相关函数

  1. socket 函数
/*成功返回 SOCKET 句柄,失败返回 INVALID_SOCKET*/
SOCKET socket(int af,int type,int protocol);
  • af :指定 SOCKET 使用的 协议族 ,一个 协议族 下面会有多种 协议 ,比如 iPv4 协议族就有 TCP 协议、UDP 协议等等,一个 协议族 下面也有多种数据传输方式。
    常见协议族有:IPv4协议族(PF_INET)、IPv6协议族(PF_INET6)、底层 SOCKET 的协议族(PF_PACKET)。
  • type:指定 SOCKET 的 数据传输方式
    常见的数据传输方式有:面向连接的(SOCK_STREAM)、面向消息的(SOCK_DGRAM

面向连接的 SOCKET :可靠的、按序传递的、基于字节的面向连接的数据传输方式
面向消息的 SOCKET:不可靠的、不按序传递的、以数据的高速传输为目的

  • protocol:指定 SOCKET 使用的 协议,该协议必须是协议族支持的协议之一。
    常见的协议类型有:TCP 协议(IPPROTO_TCP)、UDP 协议(IP_PROTO_UDP
  1. bind 函数
/*成功返回 0 ,失败返回 SOCKET_ERROR*/
int bind(SOCKET s,const struct sockaddr *name,int namelen);
  1. listen 函数
/*成功返回 0 ,失败返回 SOCKET_ERROR
* backlog:连接请求队列的长度,表示允许最多多少个连接请求进入队列
int listen(SOCKET s,int backlog);
  1. accept 函数
    调用 accept 函数时,若等待队列为空,则 accept 函数不会返回,直到队列中出现新的客户端连接。
/*成功返回 SOCKET 句柄,失败返回 INVALID_SOCKET
* addr:保存客户端地址信息
SOCKET accept(SOCKET s,struct sockaddr *addr,int *addrlen);
  1. connect 函数
    客户端调用 connect 函数后,发生以下情况之一才会返回:
    a. 服务器端接收连接请求
    b. 发生断网等异常情况而中断连接请求
/*成功返回 0 ,失败返回 SOCKET_ERROR */
int connect(SOCKET s,const struct sockaddr *name,int namelen);
  1. closesocket 函数
/*成功返回 0 ,失败返回 SOCKET_ERROR*/
int closesocket(SOCKET s);

Windows 的 I/O 函数

Windows 严格区分文件 I/O 函数和 SOCKET I/O函数,而Linux只有文件 I/O 函数。

  1. send 函数
/*成功返回传输的字节数,失败返回 SOCKET_ERROR*/
int send(SOCKET s,const char *buf,int len,int flags);
  1. recv 函数
/*成功返回接收的字节数(收到 EOF 时为 0 ),失败返回 SOCKET_ERROR*/
int WSAAPI recv(SOCKET s,char *buf,int len,int flags);

基于 Windoes 的服务器和客户端测试代码

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHandler(char *message);
int main(int argc, char *argv[])
    WSADATA wsaData;
    SOCKET hServSock, hClntSock;
    SOCKADDR_IN servAddr, clntAddr;
    int szClntAddr;
    char message[] = "Hello, world!";
    if(argc != 2)
        printf ("Usage: %s <port>\n", argv[0]);
        exit(1);
    if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
        ErrorHandler("WSAStartup failed");
    hServSock = socket(PF_INET, SOCK_STREAM, 0);
    if(hServSock == INVALID_SOCKET)
        ErrorHandler("socket error");
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(atoi(argv[1]));
    if(bind(hServSock, (SOCKADDR *)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
        ErrorHandler("bind socket error");
    if(listen(hServSock, 5) == SOCKET_ERROR)
        ErrorHandler("listen socket error");
    szClntAddr = sizeof(clntAddr);
    hClntSock = accept(hServSock, (SOCKADDR *)&clntAddr, &szClntAddr);
    if(hClntSock == INVALID_SOCKET)
        ErrorHandler("accept error");
    send(hClntSock, message, sizeof(message), 0);
    closesocket(hClntSock);
    closesocket(hServSock);
    WSACleanup();
    return 0;
void ErrorHandler(char *message)
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);

编译:菜单 Terminal > Run Build Task ,或者快捷键 Ctrl+Shift+B 。
运行:在终端键入 .\hello_server_win.exe 1234

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHandler(char *message);
int main(int argc, char *argv[])
    WSADATA wsaData;
    SOCKET hClntSock;
    SOCKADDR_IN servAddr;
    char message[30];
    int strLen;
    if(argc != 3)
        printf ("Usage: %s <IP> <port>\n", argv[0]);
        exit(1);
    if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
        ErrorHandler("WSAStartup failed");
    hClntSock = socket(PF_INET, SOCK_STREAM, 0);
    if(hClntSock == INVALID_SOCKET)
        ErrorHandler("socket error");
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = inet_addr(argv[1]);
    servAddr.sin_port = htons(atoi(argv[2]));
    if(connect(hClntSock, (SOCKADDR *)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
        ErrorHandler("connect socket error");
    strLen = recv(hClntSock, message, sizeof(message)-1, 0);
    if(strLen == EOF)
        ErrorHandler("read() error!");
    printf("Message from server: %s \n", message);
    closesocket(hClntSock);
    WSACleanup();
    return 0;
void ErrorHandler(char *message)
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);

编译:菜单 Terminal > Run Build Task ,或者快捷键 Ctrl+Shift+B 。
运行:在终端键入 .\hello_client_win.exe 127.0.0.1 1234

socket网络编程–serve的搭建 1.首先,我们要知道socket服务端搭建的大致步骤,从开始的创建套接字到数据交互都有对应的API供我们使用,在我们创建好大致框架后,首先做的是用man手册查看相应的API使用规则(在Linux终端输入man 2 socket),之后出现类似界面 可以看到API所对应的头文件,socket函数返回的是一个整型变量,我在这里定义了一个s_fd整型变量来判断返回值,并用perror函数来鉴别报错的原因和退出程序。 这里的参数AF_INET代表地址家族IPV4(因特
文章目录简介服务器网络版本1、打开网络库2、校验版本3、创建socket4、绑定地址和端口5、监听6、接受链接7、与客户端收发消息客户端1、打开网络库2、校验版本3、创建socket4、连接服务器5、与客户端收发消息类比运行结果源码链接遇到的问题头文件冲突 socket又是什么? 将网络底层复杂的协议体系,执行流程,进行了封装后就是SOCKET了,也就是说,SOCKET是我们调用协议进行通信的操作接口,将复杂的协议过程与我们编程人员分开,我们直接操作一个简单SOCKET就行了,对于底层的协议 过程细节
需要引入的头文件,和必要宏定义#define WIN32_LEAN_AND_MEAN //避免引用会产生冲突的依赖库(windows.h包含有WinSock2.h的一些宏定义,防止产生重定义错误!) #include <windows.h> //windows系统API头文件 #include <WinSock2.h>