windows网络编程(八)——重叠I/O+多线程实现简单的聊天(windows服务器端 windows客户端)

1.客户端

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 
#include 
#include  
#define BUF_SIZE 1000
#define NAME_SIZE 20

#pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll  

unsigned WINAPI SendMsg(void * arg);//发送信息函数
unsigned WINAPI RecvMsg(void * arg);//接受信息函数
void ErrorHandling(char * msg);//错误返回函数

int haveread = 0;
char NAME[50];//[名字]
char ANAME[50];
char msg[BUF_SIZE];//信息

int main(int argc, char *argv[])
{

	printf("请输入网名:");
	scanf("%s", NAME);
	WSADATA wsaData;
	SOCKET hSock;
	SOCKADDR_IN servAdr;
	HANDLE hSndThread, hRcvThread;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error!");

	hSock = socket(PF_INET, SOCK_STREAM, 0);
	memset(&servAdr, 0, sizeof(servAdr));
	servAdr.sin_family = AF_INET;
	servAdr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servAdr.sin_port = htons(1234);

	if (connect(hSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)
		ErrorHandling("connect() error");

	int resultsend;
	puts("Welcome to joining our chatting room!\n");
	sprintf(ANAME, "[%s]", NAME);

	hSndThread =
		(HANDLE)_beginthreadex(NULL, 0, SendMsg, (void*)&hSock, 0, NULL);//写线程
	hRcvThread =
		(HANDLE)_beginthreadex(NULL, 0, RecvMsg, (void*)&hSock, 0, NULL);//读线程

	WaitForSingleObject(hSndThread, INFINITE);//等待线程结束
	WaitForSingleObject(hRcvThread, INFINITE);
	closesocket(hSock);
	WSACleanup();
	system("pause");
	return 0;
}

unsigned WINAPI SendMsg(void * arg)   // send thread main
{
	SOCKET sock = *((SOCKET*)arg);
	char name_msg[NAME_SIZE + BUF_SIZE];
	char padd[2];
	fgets(padd, 2, stdin);//多余的'\n'
	printf("\n send message:");
	while (1)
	{
		{
			fgets(msg, BUF_SIZE, stdin);
			if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n"))
			{
				closesocket(sock);
				exit(0);
			}
			sprintf(name_msg, "[%s] %s", NAME, msg);
			char numofmsg = strlen(name_msg)+'0';
			char newmsg[100]; newmsg[0] = numofmsg; newmsg[1] = 0;//第一个字符表示消息的长度
			strcat(newmsg, name_msg);
			printf("信息的长度:%d", strlen(newmsg));
			int result = send(sock, newmsg, strlen(newmsg), 0);
			printf("客户端发送的消息:%s\n", newmsg);
			if (result == -1)return -1;//发送错误
		}
	}
	return NULL;
}

unsigned WINAPI RecvMsg(void * arg)  // read thread main
{
	SOCKET sock = *((SOCKET*)arg);
	char name_msg[NAME_SIZE + BUF_SIZE];
	int str_len = 0;
	while (1)
	{
		{
			char lyfstr[1000] = { 0 };
			int totalnum = 0;
			str_len = recv(sock, name_msg, 1, 0);//读取第一个字符!获取消息的长度
			name_msg[str_len] = 0;
			strcat(lyfstr, name_msg);
			if (str_len == -1)//读取错误
			{
				printf("return -1\n");
				return -1;
			}
			if (str_len == 0)//读取结束
			{
				printf("return 0\n");
				return 0;//读取结束
			}
			totalnum = name_msg[0]-'0';
			int count = 0;
			
			do
			{
				str_len = recv(sock, name_msg, 1, 0);
				
				name_msg[str_len] = 0;
				
				if (str_len == -1)//读取错误
				{
					printf("return -1\n");
					return -1;
				}
				if (str_len == 0)
				{
					printf("return 0\n");
					return 0;//读取结束
				}
				strcat(lyfstr, name_msg);
				count = str_len+count;

			} while (count < totalnum);

			lyfstr[count] = '\0';
			printf("\n");
			strcat(lyfstr, "\n");
			fputs(lyfstr, stdout);
			printf(" send message:");
			fflush(stdout);
			memset(name_msg, 0, sizeof(char));
		}
	}
	return NULL;
}

void ErrorHandling(char * msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}





2.服务器端

#include 
#include 
#include 

#pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll  


#define BUF_SIZE 1024
void CALLBACK ReadCompRoutine(DWORD, DWORD, LPWSAOVERLAPPED, DWORD);
void CALLBACK WriteCompRoutine(DWORD, DWORD, LPWSAOVERLAPPED, DWORD);
void ErrorHandling(char *message);

typedef struct
{
	SOCKET hClntSock;
	char buf[BUF_SIZE];
	WSABUF wsaBuf;
} PER_IO_DATA, *LPPER_IO_DATA;

SOCKET ALLCLIENT[100];//所有客户端
int CONNECNUM = 0;//计数
LPWSAOVERLAPPED overlap[100];//所有客户端的相关结构
int overcount = 0;


int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hLisnSock, hRecvSock;
	SOCKADDR_IN lisnAdr, recvAdr;
	LPWSAOVERLAPPED lpOvLp;
	DWORD recvBytes=0;
	LPPER_IO_DATA hbInfo;
	u_long mode = 1;
	DWORD  flagInfo = 0;
	int recvAdrSz=0;

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error!");

	hLisnSock = WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	ioctlsocket(hLisnSock, FIONBIO, &mode);   // for non-blocking socket(非阻塞套接字)

	memset(&lisnAdr, 0, sizeof(lisnAdr));
	lisnAdr.sin_family = AF_INET;
	lisnAdr.sin_addr.s_addr = htonl(INADDR_ANY);
	lisnAdr.sin_port = htons(1234);

	if (bind(hLisnSock, (SOCKADDR*)&lisnAdr, sizeof(lisnAdr)) == SOCKET_ERROR)
		ErrorHandling("bind() error");
	if (listen(hLisnSock, 5) == SOCKET_ERROR)
		ErrorHandling("listen() error");

	recvAdrSz = sizeof(recvAdr);

	while (1)
	{
		SleepEx(100, TRUE);    // for alertable wait state(设置状态)
		hRecvSock = accept(hLisnSock, (SOCKADDR*)&recvAdr, &recvAdrSz);
		if (hRecvSock == INVALID_SOCKET)
		{
			if (WSAGetLastError() == WSAEWOULDBLOCK)//只表示无连接
				continue;
			else
				ErrorHandling("accept() error");
		}
		ALLCLIENT[CONNECNUM++] = hRecvSock;
		puts("Client connected.....");
		
		lpOvLp = (LPWSAOVERLAPPED)malloc(sizeof(WSAOVERLAPPED));
		overlap[overcount++] = lpOvLp;//存入数组

		memset(lpOvLp, 0, sizeof(WSAOVERLAPPED));

		hbInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
		hbInfo->hClntSock = (DWORD)hRecvSock;
		(hbInfo->wsaBuf).buf = hbInfo->buf;
		(hbInfo->wsaBuf).len = BUF_SIZE;

		lpOvLp->hEvent = (HANDLE)hbInfo;//特殊类型转换
		if (
		WSARecv(hRecvSock, &(hbInfo->wsaBuf),
			1, &recvBytes, &flagInfo, lpOvLp, ReadCompRoutine)==SOCKET_ERROR)
		if (WSAGetLastError() == WSA_IO_PENDING)
		{
			printf("接收数据中.");
		}
	}
	closesocket(hRecvSock);
	closesocket(hLisnSock);
	WSACleanup();
	return 0;
}

void CALLBACK ReadCompRoutine(//参数:(错误信息,实际收发字节数,WSA*的lpOverlapped参数,特性信息或0)
	DWORD dwError, DWORD szRecvBytes, LPWSAOVERLAPPED lpOverlapped, DWORD flags)
{
	LPPER_IO_DATA hbInfo = (LPPER_IO_DATA)(lpOverlapped->hEvent);//类型转回
	SOCKET hSock = hbInfo->hClntSock;
	LPWSABUF bufInfo = &(hbInfo->wsaBuf);
	DWORD sentBytes;

	if (szRecvBytes == 0)
	{
		int i = 0;
		while (ALLCLIENT[i] != hSock)i++;
		ALLCLIENT[i] = -1;
		closesocket(hSock);
		free(lpOverlapped->hEvent); free(lpOverlapped);
		puts("Client disconnected.....");
	}
	else    // echo!
	{
		bufInfo->len = szRecvBytes;//存储实际接收到的字节数,在A发送一条,B发送一条,A再发送一条信息时接受到的信息会出错,不知道原因??
		int k = 0;
		for (; k < CONNECNUM; k++)
		{
			if (ALLCLIENT[k] != -1)
			{	
				int result = 0;
				result = WSASend(ALLCLIENT[k], bufInfo, 1, &sentBytes, 0, overlap[k], WriteCompRoutine);
			}
		}
	}
}

void CALLBACK WriteCompRoutine(//参数:(错误信息,实际收发字节数,WSA*的lpOverlapped参数,特性信息或0)
	DWORD dwError, DWORD szSendBytes, LPWSAOVERLAPPED lpOverlapped, DWORD flags)
{
	LPPER_IO_DATA hbInfo = (LPPER_IO_DATA)(lpOverlapped->hEvent);
	SOCKET hSock = hbInfo->hClntSock;
	LPWSABUF bufInfo = &(hbInfo->wsaBuf);
	DWORD recvBytes=0;
	DWORD flagInfo = 0;
	int result = WSARecv(hSock, bufInfo,
		1, &recvBytes, &flagInfo, lpOverlapped, ReadCompRoutine);
}

void ErrorHandling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}




3.结果

windows网络编程(八)——重叠I/O+多线程实现简单的聊天(windows服务器端 windows客户端)_第1张图片


你可能感兴趣的:(网络编程)