TCP/IP编程——多线程+非阻塞的服务实现

目录

多线程

win32程序使用多线程步骤如下

注意事项:

DDDEMOOO:

非阻塞

DDDDDEMOOOOO:

TCP多线程+select和UDP多线程+select的代码


 

多线程

process库中_beginthreadex或者 _beginthread都可以启动新线程,不过推荐使用_beginthreadex,故只介绍_beginthreadex

win32程序使用多线程步骤如下

1.#include <process.h> //for_beginthread()

2.构造多线程中线程执行的函数 thread()

3.创建句柄 开启线程 HANDLE handle1 = (HANDLE)_beginthreadex(NULL,0,thread,NULL,CREATE_SUSPENDED,*threaddr);

函数解释:

_beginthreadex

unsigned long _beginthreadex(
    void *security,    // 安全属性, 为NULL时表示默认安全性
    unsigned stack_size,    // 线程的堆栈大小, 一般默认为0
    unsigned(_stdcall *start_address)(void *),    // 所要启动的线程函数
    void *argilist, // 线程函数的参数, 是一个void*类型, 传递多个参数时用结构体
    unsigned initflag,    // 新线程的初始状态,0表示立即执行,CREATE_SUSPENDED
表示创建之后挂起
    unsigned *threaddr    // 成功返回新线程句柄, 失败返回0
);
返回一个unsigned int型的退出码。

_beginthread( 上课上的 )

uintptr_t _beginthread(
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);
返回值:
假如成功,函数将会返回一个新线程的句柄,用户可以像这样声明一个句柄变量存储返回值:
  HANDLE hStdOut = _beginthread( CheckKey, 0, NULL )。如果失败_beginthread将返回-1。

注意事项:

_beginthreadex()在创建线程失败时返回0,而_beginthread()在创建线程失败时返回-1。这一点是在检查返回结果是必须注意的。

如果是调用_beginthread()创建线程,并相应地调用_endthread()结束线程时,系统自动关闭线程句柄;而调用_beginthreadx()创建线程,并相应地调用_endthreadx()结束线程时,系统不能自动关闭线程句柄。因此调用_beginthreadx()创建线程还需程序员自己关闭线程句柄,以清除线程的地址空间。
eg.    CloseHandle(hth2);

Demo:

#include <stdio.h>
#include <windows.h>
#include <process.h>
 
unsigned int __stdcall threadDemo(LPVOID) // void *
{
    printf("我被执行啦!\n");
    return 0;
}
int main()
{
    HANDLE handle;
    handle = (HANDLE)_beginthreadex(NULL, 0, ThreadDemo, NULL, 0, NULL);
    return 0;
}

DDDEMOOO:

#include "stdafx.h" //可以将一些公共的预编译头文件放在该文件中,提前一次性编译,避免多次重复编译
#include <STDIO.H>  //头文件不区分大小写,变量区分大小写
#include <PROCESS.H>
#include <WINSOCK2.H> //在网络程序中涉及网络编程的函数都在该头文件中
//""和<>的区别:<>引用的是编译器的类库路径里面的文件,""引用的是你程序目录的相对路径中的头文件,如果找不到,再找编译器的类库路径


#pragma comment(lib, "ws2_32.lib") //该静态链接库提供了对网络相关API的支持。
//一般并不直接使用ws2_32.dll(动态链接库),原因是:动态链接库没有都编进去,每次使用前还需重新配置

WSAEVENT evToRead;//定义了一个事件,使其事件同步

#define DEFAULT_PORT  5050   //定义事件的端口号 
#define BUFFER_LENGTH 1024      //定义缓存区长度

char recv_buf[BUFFER_LENGTH];  //接收到的缓存

void ReadThread(LPVOID param)//实现子线程要做的事
{
	WSAWaitForMultipleEvents(1, &evToRead, TRUE, WSA_INFINITE, FALSE); 
	printf("recvform():%s\n", recv_buf);
	
}

int main(int argc, char* argv[])//主线程
{
	int iPort = DEFAULT_PORT;
    WSADATA wsaData;
	SOCKET sSocket;

	int iLen;

	int iRecv;
	
	struct sockaddr_in ser(作为参数输入), cli(作为参数输出);

	printf("-------------------------------------\n");
	printf("Server waiting\n");
    printf("-------------------------------------\n");
  
    
    evToRead = WSACreateEvent();                     //创建一个时间
	_beginthread(ReadThread, 0, NULL);//创建了一个子线程,使其开启,子线程和主线程并行处理
	
	
	if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {  //判断加载协议栈是否成功【先执行、再判断】,MAKEWORD(2,2)表示加载winsock2.2版。WSAStartup函数功能是:加载协议栈,初始化Ws2_32.lib,指定使用的winsock版本号
		printf("Failed to load Winsock.\n");
		return -1;
	}
	
	sSocket = socket(AF_INET, SOCK_DGRAM, 0);        //创建一个协议为IPv4,套接口协议类型为数据报,而对于TCP和UDP类型套接字而言,最后一位通常为0,只有需要创建原始套接口时候才需要设置protocol参数
	if (sSocket == INVALID_SOCKET) {
		printf("socket() Failed:%d\n", WSAGetLastError());
		WSACleanup();//当协议栈创建成功,但是套接口创建失败的时候,需要清空协议栈再返回
		return -1;
	}
		memset(&ser, 0, sizeof(ser));//在使用ser前,要用memset清空结构体【原来没有的】


	ser.sin_family = AF_INET;
	
	ser.sin_port = htons(iPort); //htons()函数把一个双字节的主机字节顺序的数转换成网络字节顺序的数
    
	ser.sin_addr.s_addr = htonl(INADDR_ANY);//htonl()函数把一个四字节主机字节顺序的数转换成网络字节顺序的数


    if (bind(sSocket, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR) {
		printf("bind() Failed:%d\n", WSAGetLastError());
         closesocket(sSocket);//绑定失败后,要释放套接口
		WSACleanup();//绑定失败后,要关闭协议栈
		return -1;
	}
	iLen = sizeof(cli);//初始化客户端地址长度参数
	
	memset(recv_buf, 0, sizeof(recv_buf));//在使用接收缓存前,对其清零。
	
	while (1) {
		
		iRecv = recvfrom(sSocket, recv_buf, BUFFER_LENGTH, 0, (SOCKADDR *)&cli, &iLen);//书本175
		if (iRecv == SOCKET_ERROR) {
			printf("recvform() Failed:%d\n", WSAGetLastError());//WSAGetLastError是指该函数返回上次发生的网络错误.当一特定的Windows Sockets API函数指出一个错误已经发生,该函数就应调用来获得对应的错误代码
			break;//跳出while循环
		} else if (iRecv == 0) {
			break;
		} else {
			printf("Accepted client IP:[%s], port:[%d]\n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port));
			WSASetEvent(evToRead);//设置这个事件,当事件到的时候,就free子线程
		}

	}

	closesocket(sSocket);//关闭套接口
	WSACleanup(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值