C语言中socket模块、线程

socket编程

windows下TCP协议

测试环境基于Win10 x64,Visual Studio

服务端代码
	1. 初始化Winsock库;
	2. 创建套接字;
	3. 配置服务端地址;
	4. 绑定套接字;
	5. 监听连接;
	6. 接收连接;
	7. 发送数据给客户端;
	8. 关闭连接。
#define _CRT_SECURE_NO_WARNINGS 1
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib, "ws2_32.lib")		// 因为用到了Winsock库,所以需要引入这个lib文件

int main()
{
	// 初始化Winsock库
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)			// 初始化Winsock库,保证正确加载和卸载库
	{
		printf("初始化Winsock失败\n");
		return 0;
	}

	// 创建套接字
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);	// 0代表默认协议
	if (serverSocket == INVALID_SOCKET)
	{
		printf("创建套接字失败\n");
		WSACleanup();
		return 0;
	}

	// 配置服务端地址
	struct sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = INADDR_ANY;				// INADDR_ANY为通配地址,0.0.0.0
	serverAddr.sin_port = htons(8080);

	// 绑定套接字
	if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		printf("绑定失败\n");
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}

	// 监听连接
	if (listen(serverSocket, 10) == SOCKET_ERROR)
	{
		printf("监听失败\n");
		closesocket(serverSocket);
		WSACleanup();
		return 1;
	}

	// 接收连接
	SOCKET clientSocket = accept(serverSocket, NULL, NULL);
	if (clientSocket == INVALID_SOCKET)
	{
		printf("连接失败\n");
		closesocket(serverSocket);
		WSACleanup();
	}

	printf("连接已建立\n");

	// 发送数据给客户端
	const char* message = "Hello, I'm Server!";
	send(clientSocket, message, (int)strlen(message), 0);

	// 关闭套接字
	closesocket(clientSocket);
	closesocket(serverSocket);
	WSACleanup();

	return 1;
}
客户端代码
	1. 初始化Winsock库;
	2. 创建套接字;
	3. 配置服务端地址;
	4. 连接到服务器;
	5. 发送数据;
	6. 接收服务端返回数据;
	7. 关闭套接字。
#define _CRT_SECURE_NO_WARNINGS 1
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib, "ws2_32.lib")		// 因为用到了Winsock库,所以需要引入这个lib文件

int main()
{
	// 初始化Winsock库
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		printf("初始化Winsock失败\n");
		return 1;
	}

	// 创建套接字
	SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (clientSocket == INVALID_SOCKET) {
		printf("创建套接字失败\n");
		WSACleanup();
		return 1;
	}

	// 配置服务端地址
	struct sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务端IP地址
	serverAddr.sin_port = htons(8080); // 服务端端口号

	// 连接到服务端
	if (connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
		printf("连接失败\n");
		closesocket(clientSocket);
		WSACleanup();
		return 1;
	}

	// 发送数据
	printf("已成功连接至服务器\n");

	// 接收服务端发送的数据
	char buffer[1024];
	int bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);
	if (bytesRead > 0) {
		buffer[bytesRead] = '\0';
		printf("服务器: %s\n", buffer);
	}

	// 关闭套接字
	closesocket(clientSocket);
	WSACleanup();

	return 1;
}

windows下UDP协议

服务端
	1. 初始化Winsock库;
	2. 创建套接字;
	3. 配置服务端地址;
	4. 绑定套接字;
	5. 接收数据;
	6. 返回数据给客户端;
	7. 关闭套接字。
#define _CRT_SECURE_NO_WARNINGS 1
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib, "ws2_32.lib")		// 因为用到了Winsock库,所以需要引入这个lib文件

int main()
{
	// 初始化Winsock库
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)			// 初始化Winsock库,保证正确加载和卸载库
	{
		printf("初始化Winsock失败\n");
		return 0;
	}

	// 创建套接字
	SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (serverSocket == INVALID_SOCKET)
	{
		printf("创建套接字失败\n");
		WSACleanup();
		return 0;
	}

	// 配置服务端地址
	struct sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = INADDR_ANY;				// INADDR_ANY为通配地址,0.0.0.0
	serverAddr.sin_port = htons(8080);

	// 绑定套接字
	if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		printf("绑定失败\n");
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}

	// 接收数据
	char buffer[1024];
	struct sockaddr_in clientAddr;
	int clientAddrSize = sizeof(clientAddr);
	int bytesRead = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientAddr, &clientAddrSize);

	if (bytesRead > 0)
	{
		buffer[bytesRead] = '\0';
		printf("客户端: %s\n", buffer);

		// 发送数据给客户端
		const char* message = "Hello from server!";
		sendto(serverSocket, message, (int)strlen(message), 0, (struct sockaddr*)&clientAddr, sizeof(clientAddr));
	}

	// 关闭套接字
	closesocket(serverSocket);
	WSACleanup();

	return 1;
}
客户端
	1. 初始化Winsock库;
	2. 创建套接字;
	3. 配置服务端地址;
	4. 发送数据给服务端;
	5. 接收服务端发送的数据;
	6. 关闭套接字。
#define _CRT_SECURE_NO_WARNINGS 1
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib, "ws2_32.lib")		// 因为用到了Winsock库,所以需要引入这个lib文件

int main()
{
	// 初始化Winsock库
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		printf("初始化Winsock失败\n");
		return 1;
	}

	// 创建套接字
	SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (clientSocket == INVALID_SOCKET) {
		printf("创建套接字失败\n");
		WSACleanup();
		return 1;
	}

	// 配置服务端地址
	struct sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务端IP地址
	// serverAddr.sin_addr.s_addr = INADDR_BROADCAST;  // 如果给其他主机发消息,用这个
	serverAddr.sin_port = htons(8080); // 服务端端口号,这里最好只用htons,因为它针对16位的主机字节,端口一般都是16位

	// 发送数据给服务端
	const char* message = "服务端你好,我是客户端!\n";
	sendto(clientSocket, message, strlen(message), 0, (struct sockaddr*)&serverAddr, sizeof(serverAddr));

	// 接收服务端发送的数据
	char buffer[1024];
	struct sockaddr_in fromAddr;
	int fromAddrSize = sizeof(fromAddr);
	int bytesRead = recvfrom(clientSocket, buffer, sizeof(buffer), 0, (struct sockaddr*)&fromAddr, &fromAddrSize);

	if (bytesRead > 0) {
		buffer[bytesRead] = '\0';
		printf("服务器: %s\n", buffer);
	}

	// 关闭套接字
	closesocket(clientSocket);
	WSACleanup();

	return 1;

}
贴一个自己项目当中用到的实例,该代码是用来和另一台主机通信的,通信要求如下
	1. 目标设备接收数据的端口是65001;
	2. 目标设备接收到数据后会有返回;
	3. 目标设备只接收18个字节的数据包;
	4. 目标设备只接收从65000端口发的数据,并且后续返回的数据也会返回到65000端口
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib") // 链接到Winsock库
#define ENTER_CODE 0xEB90149F

int main() {
    WSADATA wsaData;
    SOCKET udpSocket;

    // 初始化Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("初始化 Winsock 失败.\n");
        return -1;
    }

    // 创建UDP套接字
    udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (udpSocket == INVALID_SOCKET) {
        printf("创建 socket 失败 : %d\n", WSAGetLastError());
        WSACleanup();
        return -1;
    }

    // 设置广播
    int on = 1;
    setsockopt(udpSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));

    // 设置本地地址
    struct sockaddr_in localaddr;
    memset((char*)&localaddr, 0, sizeof(localaddr));
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons(65000);
    localaddr.sin_addr.s_addr = inet_addr("192.168.10.1");
    
    // 将套接字绑定到本地地址
    if (bind(udpSocket, (struct sockaddr*)&localaddr, sizeof(localaddr)) == SOCKET_ERROR) {
        printf("绑定失败, error code : %d\n", WSAGetLastError());
    }

    // 设置服务器地址
    struct sockaddr_in serverAddr;
    int serverAddrSize = sizeof(serverAddr);

    memset((char*)&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_BROADCAST;
    serverAddr.sin_port = htons(65001);

    // 将数据填充到buff中,注意网络字节序的转换
    unsigned char send_buff[200] = {0x00};
    int a = ENTER_CODE;
    memcpy(send_buff, &a, 4);

    // 为数据接收做准备
    unsigned char recv_buff[1072];
    struct sockaddr_in clientAddr;
    int clientAddrSize = sizeof(clientAddr);
    int recvSize;
    memset(recv_buff, 0, sizeof(recv_buff));

    while (1)
    {
        // 发送数据
        sendto(udpSocket, (char*)send_buff, 18, 0, (struct sockaddr*)&serverAddr, serverAddrSize);
        if (sendto(udpSocket, (char *)send_buff, 18, 0, (struct sockaddr*)&serverAddr, serverAddrSize) == SOCKET_ERROR) {
            printf("数据发送失败, error code : %d\n", WSAGetLastError());
        }
        else { printf("发送成功 "); }
        for (int i = 0; i < 10; i++)
        {
            printf(" %02X ", send_buff[i]);
        }
        printf("\n");
        Sleep(500);

        recvSize = recvfrom(udpSocket, (char*)recv_buff, sizeof(recv_buff), 0, (struct sockaddr*)&clientAddr, &clientAddrSize);
        if (recvSize == SOCKET_ERROR) {
            printf("接收数据失败, error code : %d\n", WSAGetLastError());
        }
        else if (recvSize > 0) {
            printf("接收到的数据: ");
            for (int i = 0; i < recvSize; i++) {
                printf("%02X ", recv_buff[i]);
            }
            printf("\n");
        }
    }

    // 清理
    closesocket(udpSocket);
    WSACleanup();

    return 0;
}

进程和线程

Windows下多线程

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <windows.h>
#include <time.h>

DWORD WINAPI threadFunction(LPVOID lpParam)
{
	int flag = 0;
	while (1)
	{
		printf("线程功能\n");
		if (flag > 10) { break; }
		else { flag += 1; }
		Sleep(100);
	}
	return 0;
}

DWORD WINAPI threadFunction2(LPVOID lpParam)
{
	int flag = 0;
	while (1)
	{
		printf("线程功能2222222\n");
		if (flag > 15) { break; }
		else { flag += 1; }
		Sleep(100);
	}
	return 0;
}

int main()
{
	HANDLE t1;				// 创建句柄
	HANDLE t2;				// 创建句柄

	/*
	LPVOID pParam1;			// 参数指针
	LPVOID pParam2;			// 参数指针
	*/

	// 创建线程
	t1 = CreateThread(
		NULL,				// 默认安全性,在大多数情况下,这个参数设置为NULL
		0,					// 指定线程的堆栈大小,如果这个参数设为0,那么系统会为新线程分配默认的堆栈大小
		threadFunction,		// 是一个函数指针,指向线程要执行的代码
		NULL,				// 线程函数的参数
		0,					// 指定如何创建线程,这个参数被设置为0,这意味着线程将立即开始执行
		NULL				// 这个参数是一个输出参数,它接收新线程的ID
	);

	// 创建线程2
	t2 = CreateThread(
		NULL,				// 默认安全性,在大多数情况下,这个参数设置为NULL
		0,					// 指定线程的堆栈大小,如果这个参数设为0,那么系统会为新线程分配默认的堆栈大小
		threadFunction2,	// 是一个函数指针,指向线程要执行的代码
		NULL,				// 线程函数的参数
		0,					// 指定如何创建线程,这个参数被设置为0,这意味着线程将立即开始执行
		NULL				// 这个参数是一个输出参数,它接收新线程的ID
	);

	if (t1 == NULL | t2 == NULL)
	{
		printf(stderr, "线程创建失败\n");
		return 0;
	}

	// 等待线程结束
	WaitForSingleObject(t1, INFINITE);
	WaitForSingleObject(t2, INFINITE);

	// 关闭线程句柄
	CloseHandle(t1);
	CloseHandle(t2);

	return 1;
}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值