Windows socket之最简单的socket程序

原文:Windows socket之最简单的socket程序

最简单的服务器的socket程序流程如下(面向连接的TCP连接 ):

1. WSAStartup(); 初始化网络库的使用。

2. socket(); 获得一个socket。

3. bind(); 把获得的socket绑定到一个ip 和端口。既然作为服务器, ip通常为本地IP127.0.0.1。

4. listen(); 监听已经绑定了指定端口的socket。

5. accept(); 接受一个来自客户端的连接。

accept()返回一个新的socket,该socket代表着本地服务器与某一个连接过来的客户端的链接。以该socket为参数,可以调用send函数往客户端发送数据,也可以调用recv函数接受客户端发送过来的函数。

最后服务器程序结束的时候调用closesocket()关闭socket, WSACleanup()终止网络库的使用,清理资源。

最简单的客户端的socket程序流程如下(同样是面向连接的TCP连接):

1. WSAStartup();初始化网络库的使用。

2. socket(); 获得一个socket。

3. connect(); 连接到一个 服务器。

连接成功后就可以收发数据了。收发完毕后调用closesocket()关闭socket,最后程序结束前调用 WSACleanup()清理资源。

下面直接上代码

需包含以下头文件和定义

#include <stdlib.h>

#include <stdio.h>

#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

#define SERVE_ADDRESS "127.0.0.1"

#define SERVE_PORT    7001

	// ----------------------------       WSAStartup()         ----------------------------//

	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		goto Main_End;
	}
	//------------------------------------------------------------------------------//

	// ----------------------------         socket()         ----------------------------//

	SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == serverSocket)
	{
		printf("failed to invoke socket, the socket returned is invalid!\n");
		goto Main_End;
	}
	// ------------------------------------------------------------------------------------//

	//----------------------------           bind()          ----------------------------//

	// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
	SOCKADDR_IN localAddr;
	localAddr.sin_family = AF_INET;
	localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
	localAddr.sin_port = htons(SERVE_PORT);
	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));

	//
	int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
	if(0 != resBind)
	{
		printf("failed to bind ! \n");
		goto Main_End;
	}
	//------------------------------------------------------------------------------------//

	//----------------------------          listen()         ----------------------------//
	int resListen = listen(serverSocket,5);
	if(0 != resListen)
	{
		printf("failed to listen! \n");
		goto Main_End;
	}

	printf("the server is listening now!\n");
	//------------------------------------------------------------------------------------//

	//----------------------------        accept()         ----------------------------//
	SOCKADDR_IN clientAddr;
	int addrLen = sizeof(clientAddr);
	SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);
	if(INVALID_SOCKET == acceptedSocket)
	{
		printf("accept error!\n");
		goto Main_End;
	}
	printf("a client has connected to the server!\n");

	//------------------------------------------------------------------------------------//

	char recvBuffer[256];
	char sendBuffer[256];

	strcpy(sendBuffer,"server:Welcome to connect !");
	int sendBufLen = strlen(sendBuffer);
	int resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);

	while(true)
	{
		if(resSend != sendBufLen)	//发送的长度与需要发送的长度不等
		{
			printf("send data error!!\n");
			break;
		}

		int recvLen = recv(acceptedSocket,recvBuffer,sizeof(recvBuffer),0);
		if(0 == recvLen)
		{
			printf("a client close the socket!\n");
			break;
		}
		else if(recvLen < 0)
		{
			printf("an error has happen when receiving\n");
			break;
		}

		recvBuffer[recvLen] = ‘\0‘;
		printf("client:%s\n",recvBuffer);

		//在客户发过来的数据前面加上server:再发回给客户端
		strcpy(sendBuffer,"server:");
		strcat(sendBuffer,recvBuffer);
		sendBufLen = strlen(sendBuffer);

		resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);
	}

	closesocket(acceptedSocket);
	closesocket(serverSocket);

 Main_End:

	WSACleanup();
	system("pause");
	return 0;

客户端代码:

	//----------------------------       WSAStartup()        ----------------------------//
	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		goto Main_End;
	}
	//------------------------------------------------------------------------------------//

	//----------------------------       socket()           ----------------------------//
	SOCKET connSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == connSocket)
	{
		printf("the socket returned is invalid!\n");
		goto Main_End;
	}
	//------------------------------------------------------------------------------------//

	//----------------------------       connect()         ----------------------------//
	//初始化struct sockaddr 结构体
	SOCKADDR_IN serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
	serverAddr.sin_port = htons(SERVE_PORT);
	memset(serverAddr.sin_zero,0x0,sizeof(serverAddr.sin_zero));

	//connect
	int resConn = connect(connSocket,(sockaddr*)&serverAddr,sizeof(serverAddr));
	if(0 != resConn)
	{
		printf("failed to connect to server!!\n");
		goto Main_End;
	}
	//------------------------------------------------------------------------------------//

	char sendBuffer[256];
	char recvBuffer[256];
	while(true)
	{
		int recvLen = recv(connSocket,recvBuffer,256,0);
		if(recvLen < 0)
		{
			printf("receive error!!\n");
			break;
		}
		else if(0 == recvLen)
		{
			printf("the server close the socket!\n");
		}

		recvBuffer[recvLen] = ‘\0‘;
		printf("the data recv:%s\n\n\n",recvBuffer);

		printf("please input what you want to send:\n");
		gets(sendBuffer);
		if(0 == strcmp(sendBuffer,"exit"))
		{
			break;
		}

		int sendDataLen = strlen(sendBuffer);
		int nDataSent = send(connSocket,sendBuffer,sendDataLen,0);
		if(nDataSent != sendDataLen)
		{
			printf("failed to send data!!\n");
			break;
		}
	}

	closesocket(connSocket);
	printf("the connection is closed!\n");

 Main_End:
	WSACleanup();
	system("pause");
	return 0;

客户端连接到服务端后,每次给服务端发送一段内容,服务器在内容前面加上server:再发送给客户端。

当客户端发送的内容是exit时,客户端程序跳出循环,关闭socket断开连接。服务端发现客户端断开连接后也关闭套接字结束程序。

当然上面程序只为了演示最简单的网络编程。有若干漏洞。

1. 服务器只能接受一个客户端连接。当然加一个循环语句进去可以重复地接受客户端的连接,但是仍然是每次只处理一个客户端连接。

2.accept, connect,send,recv函数默认均是阻塞函数。当没有客户连接到服务端时,服务端阻塞在accept函数,无法退出程序。当服务器在接受客户端的数据时,如果客户端不发送数据,也不断开连接,那么服务端阻塞在recv函数,无法退出程序。

改进该程序,使得服务端随时都可以停止服务退出程序,无论有多少个用户已经在连接。

为了多个客户端可以同时连接,最容易理解的便是利用多线程。每一个连接的客户端都用一个线程去处理它的通信。

至于为了随时可以退出服务端,不能再调用永久阻塞的函数了。利用select函数,可以阻塞指定的时间,阻塞期间不占CPU。

int select( __in int nfds, __in_out fd_set*readfds, __in_out fd_set*writefds, __in_out fd_set*exceptfds,
__in const struct timeval*timeout);

nfds

用于兼容Berkeley sockets.不用理会,随便给个0值就OK。

readfds

用于检查是否存在可读socket的的一个socket集合。可为空。

writefds

用于检查是否存在可写socket的一个socket集合。可为空。

exceptfds

用于检查是否存在有错误的socket的一个 socket集合,可为空。

timeout

TIMEVAL结构体,用于指定该函数阻塞多长时间。

在 调用select时,当readfds不为空时,当readfds中任何一个socket就绪可读时,或者当writefds不为空且writefds中任何一个socket准备就绪可写,或者当exceptfds不为空且任何一个socket发生socket错误时,select就立即返回。否则,直到timeout指定的时间过去以后才返回。

返回值,返回准备就绪的socket的个数。如果为0,说明该函数超时了,如果大于0,说明至少有一个socket就绪。如果小于0,说明发生错误了。

fd_set 是一种集合类型。

typedef struct fd_set {

u_int fd_count;               /* how many are SET? */

SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */

} fd_set;

记录着一个socket数组,以及里面的socket个数。

struct timeval是一个表示等待时间的结构体。

struct timeval {

long    tv_sec;         /* seconds */

long    tv_usec;        /* and microseconds */

};

tv_sec表示有多少秒,tv_usec表示有多少毫秒。

对于fd_set类型,用到几个宏定义函数。

FD_ZERO(fd_set*), 清空fd_set集合

FD_SET(SOCKET,fd_set*),把socket加入fd_set集合。

FD_ISSET(SOCKET,fd_set*),判断socket是否在集合fd_set中,并且socket准备就绪。

FD_CLR(SOCKET,fd_set*),如果fd_set存在该SOCKET,则移除它。

下面是改进后的服务端代码

typedef struct _ThreadInfo
{
	HANDLE hThread;
	bool bRunning;
	SOCKET sock;
}ThreadInfo;

typedef struct _AcceptThreadParam
{
	bool bRunning;
	SOCKET listeningSocket;
}AcceptThreadParam;

std::list<ThreadInfo*> g_threadInfoList;
CRITICAL_SECTION g_csForList;

DWORD WINAPI ListeningThread(LPVOID lpParameter);
DWORD WINAPI CommunicationThread(LPVOID lpParameter);

int _tmain(int argc, _TCHAR* argv[])
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	// ----------------------------       WSAStartup()         ----------------------------//

	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		return -1;
	}
	//------------------------------------------------------------------------------//

	InitializeCriticalSection(&g_csForList);

	// ----------------------------         socket()         ----------------------------//

	SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == serverSocket)
	{
		printf("failed to invoke socket, the socket returned is invalid!\n");
		goto Main_End;
	}
	// ------------------------------------------------------------------------------------//

	//----------------------------           bind()          ----------------------------//

	// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
	SOCKADDR_IN localAddr;
	localAddr.sin_family = AF_INET;
	localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
	localAddr.sin_port = htons(SERVE_PORT);
	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));

	//
	int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
	if(0 != resBind)
	{
		printf("failed to bind ! \n");
		goto Main_End;
	}
	//------------------------------------------------------------------------------------//

	//----------------------------          listen()         ----------------------------//
	int resListen = listen(serverSocket,5);
	if(0 != resListen)
	{
		printf("failed to listen! \n");
		goto Main_End;
	}

	//------------------------------------------------------------------------------------//

	AcceptThreadParam threadParam;
	threadParam.bRunning = true;
	threadParam.listeningSocket = serverSocket;

	HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);
	if(0 == hListeningThread)
	{
		printf("failed to create the listening thread!\n");
		goto Main_End;
	}
	else
	{
		printf("the server is listening now!pass any key to close the server!\n");
	}

	while(true)
	{
		char ch = getchar();
		threadParam.bRunning = false;
		DWORD resWait = WaitForSingleObject(hListeningThread,3000);
		if(WAIT_TIMEOUT == resWait)
		{
			printf("failed to wait for the listening thread exiting!\n");
		}
		else
		{
			printf("the listening thread has exited!\n");
		}

		break;

	}

Main_End:
	if(INVALID_SOCKET != serverSocket)
	{
		closesocket(serverSocket);
		serverSocket = INVALID_SOCKET;
	}

	WSACleanup();
	DeleteCriticalSection(&g_csForList);
	system("pause");
	return 0;
}

DWORD WINAPI ListeningThread(LPVOID lpParameter)
{
	AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;
	SOCKET serverSocket = pAcceptThreadParam->listeningSocket;

	while(pAcceptThreadParam->bRunning)
	{
		//----------------------------        accept()         ----------------------------//

		fd_set fdAccept;
		FD_ZERO(&fdAccept);
		FD_SET(serverSocket,&fdAccept);

		TIMEVAL acceptTimeVal;
		acceptTimeVal.tv_sec = 1;
		acceptTimeVal.tv_usec = 0;

		int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);
		if(selRes > 0)
		{
			SOCKADDR_IN clientAddr;
			int addrLen = sizeof(clientAddr);
			SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);
			if(INVALID_SOCKET == acceptedSocket)
			{
				printf("accept error!\n");
				break;
			}
			printf("a client has connected to the server!\n");

			ThreadInfo* pTI = new ThreadInfo;
			pTI->bRunning = true;
			pTI->sock = acceptedSocket;
			pTI->hThread = CreateThread(0,0,CommunicationThread,(LPVOID)pTI,0,0);
			if(0 == pTI->hThread)
			{
				printf("failed to create a thread!\n");
				delete pTI;
				pTI = 0;
			}
			else
			{
				EnterCriticalSection(&g_csForList);
				g_threadInfoList.push_back(pTI);
				LeaveCriticalSection(&g_csForList);
			}
		}
		else if(selRes < 0)
		{
			printf("an error has occured when listening !\n");
			break;
		}

	}

	std::list<ThreadInfo*> tempList;

	EnterCriticalSection(&g_csForList);
	std::list<ThreadInfo*>::iterator listIter;
	for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++)
	{
		(*listIter)->bRunning = false;
		tempList.push_back(*listIter);
	}

	g_threadInfoList.clear();
	LeaveCriticalSection(&g_csForList);

	int nSuccessfullyExit = 0;
	for(listIter = tempList.begin(); listIter != tempList.end(); listIter++)
	{
		DWORD resWait = WaitForSingleObject((*listIter)->hThread,2000);
		if(WAIT_TIMEOUT == resWait)
		{
			printf("failed to wait for a communication thread exiting!\n");
		}
		else
		{
			nSuccessfullyExit++;
		}

		delete (*listIter);
	}

	printf("succeed waiting for %d thread exiting!\n",nSuccessfullyExit);
	tempList.clear();

	printf("listening thread is exiting!\n");
	return 0;
}

DWORD WINAPI CommunicationThread(LPVOID lpParameter)
{
	ThreadInfo* pThreadInfo = (ThreadInfo*)lpParameter;

	SOCKET clientSocket = pThreadInfo->sock;
	fd_set fdRead,fdWrite;
	FD_ZERO(&fdRead);
	FD_ZERO(&fdWrite);

	FD_SET(clientSocket,&fdRead);
	FD_SET(clientSocket,&fdWrite);

	TIMEVAL sendTimeVal;
	sendTimeVal.tv_sec = 0;
	sendTimeVal.tv_usec = 500;

	int selRes = select(0,0,&fdWrite,0,&sendTimeVal);
	if(selRes <= 0)
	{
		goto ThreadOver;
	}

	char recvBuffer[256];
	char sendBuffer[256];
	strcpy(sendBuffer,"server:Welcome to connect !");
	int sendBufLen = strlen(sendBuffer);
	int resSend = send(clientSocket,sendBuffer,sendBufLen,0);
	if(resSend != sendBufLen)
	{
		printf("there are %d bytes to send, but it just succeeded sending %d bytes!\n",sendBufLen,resSend);
		goto ThreadOver;
	}

	while(pThreadInfo->bRunning)
	{
		FD_ZERO(&fdRead);
		FD_SET(pThreadInfo->sock,&fdRead);
		TIMEVAL recvTimeVal;
		recvTimeVal.tv_sec = 0;
		recvTimeVal.tv_usec = 500;

		int recvSelRes = select(0,&fdRead,0,0,&recvTimeVal);
		if(recvSelRes < 0)
		{
			printf("socket error when receiving!\n");
			break;
		}
		else if(recvSelRes > 0)
		{
			int recvLen = recv(clientSocket,recvBuffer,sizeof(recvBuffer),0);
			if(0 == recvLen)
			{
				printf("a client close the socket!\n");
				break;
			}
			else if(recvLen < 0)
			{
				printf("an error has happen when recving\n");
				break;
			}
			else
			{
				recvBuffer[recvLen] = ‘\0‘;
				printf("a client:%s\n",recvBuffer);
				strcpy(sendBuffer,"server:");
				strcat(sendBuffer,recvBuffer);
				sendBufLen = strlen(sendBuffer);

				FD_ZERO(&fdWrite);
				FD_SET(pThreadInfo->sock,&fdWrite);

				sendTimeVal.tv_sec = 0;
				sendTimeVal.tv_usec = 500;

				int sendSelRes = select(0,0,&fdWrite,0,&sendTimeVal);
				if(sendSelRes > 0)
				{
					int bytesSent = send(clientSocket,sendBuffer,sendBufLen,0);
					if(bytesSent != sendBufLen)
					{
						printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);
						break;
					}
				}
				else
				{
					printf("failed to send in 500 ms!\n");
					break;
				}

			}
		}
	}

ThreadOver:
	closesocket(pThreadInfo->sock);
	bool bMainThreadWaiting = true;

	EnterCriticalSection(&g_csForList);
	std::list<ThreadInfo*>::iterator listIter;
	for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++)
	{
		if(pThreadInfo == (*listIter))
		{
			bMainThreadWaiting = false;
			g_threadInfoList.erase(listIter);
			break;
		}
	}
	LeaveCriticalSection(&g_csForList);

	if(false == bMainThreadWaiting)
	{
		CloseHandle(pThreadInfo->hThread);
		delete pThreadInfo;
		pThreadInfo = 0;
	}

	return 0;
}

前面的代码与之前的一样,改变的地方在于accept的地方。对于一个监听的socket,如果该socket可读,说明有用户连接过来了。

全局维护了一个纪录创建的线程的信息的链表,每创建一个线程都有一个标识该线程是否应该继续循环执行的bool变量。当bRunning变为false的时候,线程函数跳出循环,返回。

当需要停止服务端运行时,服务端只需要按任何一个键和回车,就会通知线程退出,并且调用WaitForSingleObject(),来确认线程已退出。还有利用了 EnterCriticalSection()和LeaveCriticalSection()临界区函数来保证只有一个线程在操作全局的链表。

使用多线程要消耗一定的资源。对于fd_set,默认最多可以容纳64个socket.所以可以用1个线程去处理64个客户端的连接。而不必每个客户端都创建一个线程。

代码如下:

typedef struct _AcceptThreadParam
{
	bool bRunning;
	SOCKET listeningSocket;
}AcceptThreadParam;

#define SOCKET_ARRAY_SIZE 64

SOCKET g_socketArray[SOCKET_ARRAY_SIZE];
int g_socketCount = 0;
CRITICAL_SECTION g_csForSocketArray;

DWORD WINAPI ListeningThread(LPVOID lpParameter);
DWORD WINAPI CommunicationThread(LPVOID lpParameter);

int _tmain(int argc, _TCHAR* argv[])
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	// ----------------------------       WSAStartup()         ----------------------------//

	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		return -1;
	}
	//------------------------------------------------------------------------------//

	InitializeCriticalSection(&g_csForSocketArray);
	g_socketCount = 0;

	// ----------------------------         socket()         ----------------------------//

	SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == serverSocket)
	{
		printf("failed to invoke socket, the socket returned is invalid!\n");
		goto Main_End;
	}
	// ------------------------------------------------------------------------------------//

	//----------------------------           bind()          ----------------------------//

	// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
	SOCKADDR_IN localAddr;
	localAddr.sin_family = AF_INET;
	localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
	localAddr.sin_port = htons(SERVE_PORT);
	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));

	//
	int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
	if(0 != resBind)
	{
		printf("failed to bind ! \n");
		goto Main_End;
	}
	//------------------------------------------------------------------------------------//

	//----------------------------          listen()         ----------------------------//
	int resListen = listen(serverSocket,5);
	if(0 != resListen)
	{
		printf("failed to listen! \n");
		goto Main_End;
	}

	//------------------------------------------------------------------------------------//

	AcceptThreadParam threadParam;
	threadParam.bRunning = true;
	threadParam.listeningSocket = serverSocket;

	bool bCommunicationThreadRunning = true;

	HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);
	HANDLE hCommunicationThread = CreateThread(0,0,CommunicationThread,&bCommunicationThreadRunning,0,0);
	if(0 == hListeningThread || 0 == hCommunicationThread)
	{
		printf("failed to create a thread!\n");

		if(0 != hListeningThread)
		{
			threadParam.bRunning = false;
			WaitForSingleObject(hListeningThread,2000);
			CloseHandle(hListeningThread);
		}

		if(0 != hCommunicationThread)
		{
			bCommunicationThreadRunning = false;
			WaitForSingleObject(hCommunicationThread,2000);
			CloseHandle(hCommunicationThread);
		}

		goto Main_End;
	}
	else
	{
		printf("the server is listening now!pass any key to close the server!\n");
	}

	while(true)
	{
		char ch = getchar();
		threadParam.bRunning = false;
		bCommunicationThreadRunning = false;
		DWORD resWait = WaitForSingleObject(hListeningThread,3000);
		if(WAIT_TIMEOUT == resWait)
		{
			printf("failed to wait for the listening thread exiting!\n");
		}
		else
		{
			printf("the listening thread has exited!\n");
		}

		CloseHandle(hListeningThread);

		resWait = WaitForSingleObject(hCommunicationThread,3000);
		if(WAIT_TIMEOUT == resWait)
		{
			printf("failed to wait for the communication thread exiting!\n");
		}
		else
		{
			printf("the communication thread has exited!\n");
		}

		CloseHandle(hCommunicationThread);

		break;

	}

Main_End:
	if(INVALID_SOCKET != serverSocket)
	{
		closesocket(serverSocket);
		serverSocket = INVALID_SOCKET;
	}

	WSACleanup();
	DeleteCriticalSection(&g_csForSocketArray);
	system("pause");
	return 0;
}

DWORD WINAPI ListeningThread(LPVOID lpParameter)
{
	AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;
	SOCKET serverSocket = pAcceptThreadParam->listeningSocket;

	while(pAcceptThreadParam->bRunning)
	{
		//----------------------------        accept()         ----------------------------//

		fd_set fdAccept;
		FD_ZERO(&fdAccept);
		FD_SET(serverSocket,&fdAccept);

		TIMEVAL acceptTimeVal;
		acceptTimeVal.tv_sec = 1;
		acceptTimeVal.tv_usec = 0;

		int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);
		if(selRes > 0)
		{
			SOCKADDR_IN clientAddr;
			int addrLen = sizeof(clientAddr);
			SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);
			if(INVALID_SOCKET == acceptedSocket)
			{
				printf("accept error!\n");
				break;
			}
			printf("a client has connected to the server!\n");

			fd_set fdWrite;
			FD_ZERO(&fdWrite);
			FD_SET(acceptedSocket,&fdWrite);
			TIMEVAL writeTimeVal;
			writeTimeVal.tv_sec = 0;
			writeTimeVal.tv_usec = 500;

			int writeSelRes = select(0,0,&fdWrite,0,&writeTimeVal);
			if(writeSelRes > 0)
			{
				int sendBufferLen = strlen("server:Welcome to connect!");
				int bytesSent = send(acceptedSocket,"server:Welcome to connect!",sendBufferLen,0);
				if(bytesSent == sendBufferLen)
				{
					EnterCriticalSection(&g_csForSocketArray);
					if(g_socketCount < 64)
					{
						g_socketArray[g_socketCount] = acceptedSocket;
						g_socketCount++;
					}
					else
					{
						printf("the server has accepted more than 64 clients!\n");
						closesocket(acceptedSocket);
					}
					LeaveCriticalSection(&g_csForSocketArray);
				}
				else
				{
					printf("send error, there are %d bytes to be sent, but only %d bytes are sent!\n",sendBufferLen,bytesSent);
					closesocket(acceptedSocket);
				}
			}
			else
			{
				printf("select error of can not wait for sending data when select!\n");
				closesocket(acceptedSocket);
			}
		}
		else if(selRes < 0)
		{
			printf("an error has occured when listening !\n");
			break;
		}

	}

	printf("listening thread is exiting!\n");
	return 0;
}

DWORD WINAPI CommunicationThread(LPVOID lpParameter)
{
	bool* pBRunning = (bool*)lpParameter;
	char recvBuffer[256];
	char tempBuffer[256];

	while(true == *pBRunning)
	{
		int currentSocketCount = 0;

		EnterCriticalSection(&g_csForSocketArray);

		if(0 == g_socketCount)
		{
			LeaveCriticalSection(&g_csForSocketArray);
			Sleep(200);
			continue;
		}

		currentSocketCount = g_socketCount;
		LeaveCriticalSection(&g_csForSocketArray);

		fd_set fdRead;
		FD_ZERO(&fdRead);
		for(int i = 0; i < currentSocketCount; i++)
		{
			FD_SET(g_socketArray[i],&fdRead);
		}

		TIMEVAL readTimeVal;
		readTimeVal.tv_sec = 1;
		readTimeVal.tv_usec = 0;

		int selRes = select(0,&fdRead,0,0,&readTimeVal);
		if(selRes > 0)
		{
			for(int i = 0; i < currentSocketCount; i++)
			{
				if(FD_ISSET(g_socketArray[i],&fdRead) != 0)
				{
					int bytesRecv = recv(g_socketArray[i],recvBuffer,sizeof(recvBuffer),0);
					if(bytesRecv > 0)
					{
						recvBuffer[bytesRecv] = ‘\0‘;
						printf("the %d client: %s\n",i + 1,recvBuffer);
						sprintf(tempBuffer,"the server:%s",recvBuffer);

						fd_set fdWrite;
						FD_ZERO(&fdWrite);
						FD_SET(g_socketArray[i],&fdWrite);
						TIMEVAL writeTimeVal;
						writeTimeVal.tv_sec = 0;
						writeTimeVal.tv_usec = 500;

						int writeSelRes = select(g_socketArray[i],0,&fdWrite,0,&writeTimeVal);
						if(writeSelRes > 0)
						{
							int sendBufLen = strlen(tempBuffer);
							int bytesSent = send(g_socketArray[i],tempBuffer,sendBufLen,0);
							if(bytesSent == sendBufLen)
							{
								break;
							}
							else
							{
								printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);
							}
						}
						else
						{
							printf("select error!\n");
						}
					}
					else if(0 == bytesRecv)
					{
						printf("the %d client has closed the socket!\n",i + 1);
					}
					else
					{
						printf("recv error!\n");
					}

					closesocket(g_socketArray[i]);
					EnterCriticalSection(&g_csForSocketArray);
					g_socketArray[i] = g_socketArray[g_socketCount - 1];
					g_socketCount--;
					LeaveCriticalSection(&g_csForSocketArray);
				}
			}
		}
		else if(selRes < 0)
		{
			printf("select error in communication thread!\n");
		}
	}

	EnterCriticalSection(&g_csForSocketArray);
	for(int i = 0; i < g_socketCount; i++)
	{
		closesocket(g_socketArray[i]);
	}
	LeaveCriticalSection(&g_csForSocketArray);

	printf("the communication thread is exiting!\n");
	return 0;
}

完成的功能一样。只需要一个线程就可以处理多个客户端了。

还可以用异步IO来实现该服务器,以下是用完成端口来实现同样功能的服务器。

typedef struct _RepeatAcceptingThreadParam
{
	SOCKET listeningSocket;
	bool* pBRunning;
}RepeatAcceptingThreadParam;

typedef struct _CompletionPortWorkerThreadParam
{
	HANDLE hCompletionPort;
	bool* pBRunning;
}CompletionPortWorkerThreadParam;

#define MESSAGE_BUF_SIZE 1024

enum OPERATION_TYPE
{
	OPERATION_SEND,
	OPERATION_RECV
};

typedef struct
{
	SOCKET sock;
	WSAOVERLAPPED overlap;
	WSABUF wsaBuf;
	char message[1024];
	DWORD bytesRecv;
	DWORD flags;
	OPERATION_TYPE operationType;
}PER_IO_OPERATION_DATA;

//global vector, which saves the information of the client sockets connected to the server
std::vector<PER_IO_OPERATION_DATA*> g_perIoDataPointerVec;

//accept sockets connected to the server‘s listening socket in a recycle - while
DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter);	

//the worker thread that deal with the communications between the server and the clients.
DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter);

int _tmain(int argc,_TCHAR* argv[])
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	// ----------------------------       WSAStartup()         ----------------------------//

	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		return -1;
	}
	//------------------------------------------------------------------------------//

	// ----------------------------         socket()         ----------------------------//

	SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == serverSocket)
	{
		printf("failed to invoke socket, the socket returned is invalid!\n");
		return -1;
	}
	// ------------------------------------------------------------------------------------//

	//----------------------------           bind()          ----------------------------//

	// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
	SOCKADDR_IN localAddr;
	localAddr.sin_family = AF_INET;
	localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
	localAddr.sin_port = htons(SERVE_PORT);
	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));

	//
	int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
	if(0 != resBind)
	{
		printf("failed to bind ! \n");
		closesocket(serverSocket);
		return -1;
	}
	//------------------------------------------------------------------------------------//

	//----------------------------          listen()         ----------------------------//
	int resListen = listen(serverSocket,5);
	if(0 != resListen)
	{
		printf("failed to listen! \n");
		closesocket(serverSocket);
		return -1;
	}

	//------------------------------------------------------------------------------------//

	bool bRepeatAcceptingThreadRunning = true;	// a bool variable that take control of terminating the RepeatAcceptingThread.

	//init the parameter for the RepeatAcceptingThread.
	RepeatAcceptingThreadParam rtp;
	rtp.listeningSocket = serverSocket;
	rtp.pBRunning = &bRepeatAcceptingThreadRunning;

	HANDLE hRepeatAcceptingThread = CreateThread(0,0,RepeatAcceptingThread,&rtp,0,0);
	if(0 == hRepeatAcceptingThread)
	{
		printf("failed to create the repeat-accepting thread!\n");
		closesocket(serverSocket);
		return -1;
	}

	printf("the repeat-accepting thread has run!\n");

	while(true)
	{
		// pass any key
		char ch = getchar();
		bRepeatAcceptingThreadRunning = false;//to notify the RepeatAcceptingThread to exit safely 

		DWORD waitRes = WaitForSingleObject(hRepeatAcceptingThread,3000);
		if(WAIT_TIMEOUT == waitRes)
		{
			printf("failed to wait for the repeatAcceptingThread exiting!\n");
		}
		else
		{
			printf("the repeat accepting thread has exited!\n");
		}

		CloseHandle(hRepeatAcceptingThread);

		break;
	}

	system("pause");
	return 0;
}

DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter)
{
	//get the parameters passed by the creator of the thread.
	RepeatAcceptingThreadParam* pParam = (RepeatAcceptingThreadParam*)lpParameter;
	SOCKET listeningSocket = pParam->listeningSocket;
	bool* pStillRun = pParam->pBRunning;

	// create a completion port
	HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
	if(0 == hCompletionPort)
	{
		printf("failed to CreateIoCompletionPort!\n");
		return -1;
	}

	// a bool variable for notifying the worker threads of exiting.
	bool bWorkThreadRunning = true;

	// a vector of HANDLEs,which will be used for synchronization of waiting the worker threads to exit.
	std::vector<HANDLE> threadHandlesVec;

	SYSTEM_INFO systemInfo;
	GetSystemInfo(&systemInfo);

	//the parameter to be passed to the worker thread.
	CompletionPortWorkerThreadParam cpwtp;
	cpwtp.pBRunning = &bWorkThreadRunning;
	cpwtp.hCompletionPort = hCompletionPort;

	for(int i = 0; i < systemInfo.dwNumberOfProcessors; i++)
	{
		HANDLE hThread = CreateThread(0,0,CompletionPortWorkerThread,&cpwtp,0,0);
		if(0 == hThread)
		{
			printf("failed to create a completion port worker thread!\n");
			bWorkThreadRunning = false;

			// terminate all threads created safely.
			std::vector<HANDLE>::iterator vecIter;
			for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++)
			{
				DWORD waitRes = WaitForSingleObject(*vecIter,2000);
				if(WAIT_TIMEOUT == waitRes)
				{
					printf("failed the wait for the completion port worker thread!\n");
				}

				CloseHandle(*vecIter);
			}

			threadHandlesVec.clear();

			CloseHandle(hCompletionPort);
			return -1;
		}
		else
		{
			threadHandlesVec.push_back(hThread);	//add the handle to the vector
		}
	}

	printf("succeed creating completion port worker threads!\n");

	while(true == *pStillRun)
	{
		fd_set fdAccept;
		FD_ZERO(&fdAccept);
		FD_SET(listeningSocket,&fdAccept);

		TIMEVAL acceptTimeVal;
		acceptTimeVal.tv_sec = 1;
		acceptTimeVal.tv_usec = 0;

		int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);
		if(selRes > 0)	// a client connected
		{
			SOCKADDR_IN clientAddr;
			int addrLen = sizeof(clientAddr);

			SOCKET acceptedSocket = WSAAccept(listeningSocket,(struct sockaddr*)&clientAddr,&addrLen,0,0);
			if(0 == acceptedSocket)
			{
				printf("failed to accept a connection!\n");
			}
			else
			{
				printf("a clent %s:%d has connected!\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));

				PER_IO_OPERATION_DATA* perIoData = new PER_IO_OPERATION_DATA;
				if(0 == perIoData)
				{
					closesocket(acceptedSocket);
					printf("failed to new a struct! there is not enough memory!\n\n");
				}
				else
				{
					//associate the newly connected client socket with the completion port.
					if(0 == CreateIoCompletionPort((HANDLE)acceptedSocket,hCompletionPort,(ULONG_PTR)perIoData,0))
					{
						printf("failed to associate the newly connected client socket with the completion port!\n");
						closesocket(acceptedSocket);
						delete perIoData;
						perIoData = 0;
					}
					else
					{
						//associated successfully, Set the information of the client socket in A PER_IO_OPERATION_DATA struct.
						//when a IO operation is completed, we can get notified with the struct to be one of the parameters.
						perIoData->sock = acceptedSocket;
						perIoData->operationType = OPERATION_SEND;
						perIoData->wsaBuf.buf = perIoData->message;
						perIoData->overlap.hEvent = INVALID_HANDLE_VALUE;

						strcpy(perIoData->message,"Welcome to connect to the server!");
						perIoData->wsaBuf.len = strlen(perIoData->message);

						int sendRes = WSASend(acceptedSocket,&(perIoData->wsaBuf),1,&(perIoData->bytesRecv),0,0,0);
						if(0 == sendRes)	//finished immediately
						{
							// asynchronously invoke a receive operation. When the reception finished,we can get its information by
							// invoking GetQueuedCompletionStatus()
							perIoData->wsaBuf.buf = perIoData->message;
							perIoData->wsaBuf.len = MESSAGE_BUF_SIZE;
							perIoData->flags = 0;
							perIoData->operationType = OPERATION_RECV;
							ZeroMemory(&perIoData->overlap,sizeof(perIoData->overlap));

							int recvRes = WSARecv(acceptedSocket,&perIoData->wsaBuf,1,&perIoData->bytesRecv,&perIoData->flags,&perIoData->overlap,0);
							if(0 == recvRes)	//the receiving operation finished immediately , the information of the operation has been queued.
							{
								g_perIoDataPointerVec.push_back(perIoData);
							}
							else if(SOCKET_ERROR == recvRes && WSA_IO_PENDING == WSAGetLastError())	//the receiving operation will finish later
							{
								g_perIoDataPointerVec.push_back(perIoData);
							}
							else
							{
								printf("failed to WSARecv!\n");
								closesocket(acceptedSocket);
								delete perIoData;
								perIoData = 0;
							}

						}
						else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError())		//the sending operation will finish later
						{
							g_perIoDataPointerVec.push_back(perIoData);
						}
						else
						{
							//int lastErr = WSAGetLastError();
							printf("send data error!\n");
							closesocket(acceptedSocket);
							delete perIoData;
							perIoData = 0;
						}
					}
				}

			}
		}
		else if(selRes < 0)
		{
			printf("select error!\n");
		}
	}

	bWorkThreadRunning = false;	//notifies the worker threads of exiting

	// terminate all threads created safely.
	std::vector<HANDLE>::iterator vecIter;
	for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++)
	{
		DWORD waitRes = WaitForSingleObject(*vecIter,2000);
		if(WAIT_TIMEOUT == waitRes)
		{
			printf("failed the wait for the completion port worker thread!\n");
		}

		CloseHandle(*vecIter);
	}

	threadHandlesVec.clear();

	CloseHandle(hCompletionPort);

	//delete the structs of PER_IO_OPERATION_DATA newed for clients connected.
	std::vector<PER_IO_OPERATION_DATA*>::iterator pIoDataPointerIter;
	for(pIoDataPointerIter = g_perIoDataPointerVec.begin(); pIoDataPointerIter != g_perIoDataPointerVec.end(); pIoDataPointerIter++)
	{
		closesocket((*pIoDataPointerIter)->sock);
		delete (*pIoDataPointerIter);
		*pIoDataPointerIter = 0;
	}

	g_perIoDataPointerVec.clear();

	printf(" the repeat accepting thread is exiting!\n");
	return 0;
}

bool ReleaseIOOperationData(PER_IO_OPERATION_DATA* & pDataToBeDeleted)
{
	bool retVal = false;

	std::vector<PER_IO_OPERATION_DATA*>::iterator vecIter;
	for(vecIter = g_perIoDataPointerVec.begin(); vecIter != g_perIoDataPointerVec.end(); vecIter++)
	{
		if(pDataToBeDeleted == (*vecIter))
		{
			g_perIoDataPointerVec.erase(vecIter);
			closesocket(pDataToBeDeleted->sock);
			delete pDataToBeDeleted;
			pDataToBeDeleted = 0;
			retVal = true;
			break;
		}
	}

	return retVal;
}

DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter)
{
	CompletionPortWorkerThreadParam* pParam = (CompletionPortWorkerThreadParam*)lpParameter;
	bool* pStillRun = pParam->pBRunning;
	HANDLE hCompletionPort = pParam->hCompletionPort;

	DWORD dwBytesTransfered;
	PER_IO_OPERATION_DATA* pIoData;
	WSAOVERLAPPED* pOverlap;

	while(true == *pStillRun)
	{
		dwBytesTransfered = 0;
		pIoData = 0;
		pOverlap = 0;
		BOOL bGetStatus = GetQueuedCompletionStatus(hCompletionPort,&dwBytesTransfered,(PULONG_PTR)&pIoData,&pOverlap,500);
		if(FALSE == bGetStatus)
		{
			if(0 == pOverlap)	//did not get a packet from the queue.
			{
				continue;
			}
			else
			{
				//get a packet for a failed I/O operation.
			}
		}

		if(OPERATION_SEND == pIoData->operationType)
		{
			if(0 == dwBytesTransfered)	//a packet for a failed I/O operation.
			{
				printf("the client %d has close the socket!\n",pIoData->sock);
				ReleaseIOOperationData(pIoData);
			}
			else
			{
				// receive operation.
				pIoData->operationType = OPERATION_RECV;
				pIoData->wsaBuf.buf = pIoData->message;
				pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;
				pIoData->flags = 0;
				ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));

				int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);
				if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError())
				{
					printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);
					ReleaseIOOperationData(pIoData);
				}
			}

		}
		else if(OPERATION_RECV == pIoData->operationType)
		{
			if(0 == dwBytesTransfered)	//a packet for a failed I/O operation.
			{
				printf("the client %d has close the socket!\n",pIoData->sock);
				ReleaseIOOperationData(pIoData);
			}
			else
			{
				// show the data received
				pIoData->message[dwBytesTransfered] = ‘\0‘;
				printf("the client %d:%s \n",pIoData->sock,pIoData->message);

				//send back the data received add a "server:" in the front
				char tempBuf[MESSAGE_BUF_SIZE];
				sprintf(tempBuf,"server:%s",pIoData->message);
				strcpy(pIoData->message,tempBuf);

				pIoData->operationType = OPERATION_SEND;
				pIoData->wsaBuf.buf = pIoData->message;
				pIoData->wsaBuf.len = strlen(pIoData->message);

				int sendRes = WSASend(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,0,0,0);
				if(0 == sendRes)
				{
					pIoData->operationType = OPERATION_RECV;
					pIoData->wsaBuf.buf = pIoData->message;
					pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;
					pIoData->flags = 0;
					ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));

					int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);
					if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError())
					{
						printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);
						ReleaseIOOperationData(pIoData);
					}
				}
				else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError())
				{

				}
				else
				{
					printf("send error, maybe the client %d has close the socket!\n",pIoData->sock);
					ReleaseIOOperationData(pIoData);
				}
			}

		}
	}

	printf("a completion port thread is exiting!\n");
	return 0;
}
时间: 2024-10-03 14:04:44

Windows socket之最简单的socket程序的相关文章

基于socket实现的简单的聊天程序

记得八年前第一次使用socket做的一个五子棋程序,需要序列化棋子对象,传递到对方的电脑上. 一个偶然的机会,第二次使用socket做点事情.先看聊天服务器端的实现: 服务器端要实现以下功能:      1.启动服务,开启监听      2.持续不断地接收消息      3.发送消息 启动服务,创建监听socket,绑定Ip和端口: 1 /// <summary> 2 /// 启动服务 3 /// </summary> 4 private void Start() 5 { 6 so

Java网络编程以及简单的聊天程序

网络编程技术是互联网技术中的主流编程技术之一,懂的一些基本的操作是非常必要的.这章主要讲解网络编程,UDP和Socket编程,以及使用Socket做一个简单的聊天软件. 全部代码下载:链接 1.网络编程简要概述: 网络编程实质实质就是两个(或多个)设备(例如计算机)之间的数据传输.而要实现两台计算机通过互联网连接进行数据传输,必输要满足计算机网络的5层协议(物理层,数据链路层,网络层,运输层,应用层):当然有划分可能不同,但现在大家比较容易接受理解的是五层模型.而其中前三层物理层,数据链路层以及

linux下java程序与C语言程序通过SOCKET通信的简单例子

linux下java程序与C语言程序通过SOCKET通信的简单例子 今天上午实验了java程序与c语言程序通过socket进行通信.由于没学过java,因此只是编写了C语言端的代码,java端的代码是从网上别的文章中找的,经过少量修改后与C语言端程序通信成功. 本例中C语言端作为服务器,java端作为客户端 代码如下: /****************** server program *****************/ #include <stdio.h> #include <sy

python网络编程基础--socket的简介,以及使用socket来搭建一个简单的udp小程序

socket介绍: socket(简称套接字),是进程间通讯的一个工具,他能实现把数据从一方传输到另一方,完成不同电脑上进程之间的通讯,它好比数据的搬运工.socket应用:不夸张来说,只要跟网络相关的应用程序或者软件都使用到了socket, 比如:微信,qq等使用socket创建udp网络程序的流程:1,创建客户端套接字2,发送/接收数据3,关闭套接字使用程序展现流程 :(使用的 ide为pycharm):1,首先创建socket,在 Python 中 使用socket 模块的函数 socke

利用java的Socket实现一个简单hello/hi聊天程序

利用java的Socket实现一个简单hello/hi聊天程序 首先,我们来用java实现一个简单的hello/hi聊天程序.在这个程序里,我学习到了怎么用socket套接套接字来进行编程.简单理解了一些关于socket套接字和底层调用的关系.关于java的封装思想,我学会了一些东西,java里真的是万物皆对象.还学到了一点多线程的知识. TCP 在这里,不得不先介绍以下TCP.TCP是传输层面向连接的协议.提供了端到端的进程之间的通信方式.TCP在通信之前要先建立连接.这里我们称这个建立连接的

socket编程,简单多线程服务端测试程序

socket编程,简单多线程服务端测试程序 前些天重温了MSDN关于socket编程的WSAStartup.WSACleanup.socket.closesocket.bind.listen.accept.recv.send等函数的介绍,今天写了一个CUI界面的测试程序(依赖MFC)作为补充.程序功能简介如下: 1:一个线程做监听用. 2:监听线程收到客户端连接后,创建新线程接收客户端数据.所有对客户端线程将加入容器,以便管理. 3:服务端打印所有客户端发来的信息. 4:服务端CUI界面输入数字

【总结】学习Socket编写的聊天室小程序

1.前言 在学习Socket之前,先来学习点网络相关的知识吧,自己学习过程中的一些总结,Socket是一门很高深的学问,本文只是Socket一些最基础的东西,大神请自觉绕路. 传输协议 TCP:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的.可靠的.基于字节流的运输层(Transport layer)通信协议. 特点: 面向连接的协议,数据传输必须要建立连接,所以在TCP中需要连接时间. 传输数据大小限制,一旦连接建立,双方可以按统一的

C#版 Socket编程(最简单的Socket通信功能)

原文://http://blog.csdn.net/linybo/article/details/51141960 示例程序是同步套接字程序,功能很简单,只是客户端发给服务器一条信息,服务器向客户端返回一条信息:这里只是一个简单的示例,是一个最基本的socket编程流程,在接下来的文章中,会依次记录套接字的同步和异步,以及它们的区别. 第一步:用指定的端口号和服务器的ip建立一个EndPoint对像: 第二步:建立一个Socket对像: 第三步:用socket对像的Bind()方法绑定EndPo

Swift和C混合Socket编程实现简单的ping命令

这个是用Mac下的Network Utility工具实现ping命令,用Wireshark抓取的ICMP数据包: 发送ICMP数据包内容 接受ICMP数据包内容 一.icmp结构 要真正了解ping命令实现原理,就要了解ping命令所使用到的TCP/IP协议.ICMP(Internet Control Message,网际控制报文协议)是为网关和目标主机而提供的一种差错控制机制,使它们在遇到差错时能把错误报告给报文源发方.ICMP协议是IP层的 一个协议,但是由于差错报告在发送给报文源发方时可能