TCP/IP C 语言实现单个客户端和服务端 TCP 通信

 这是多线程服务端

#include 
#include 
#include 
#include 
#include 
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable : 4996)	
// 客户端结构体
typedef struct ThreadNode
{
	int index;
	pthread_t* Thread;
	SOCKET Client;
	struct ThreadNode * next;
	ThreadNode()
	{
		index = 0;
		this->next = NULL;
	}
} hThread;
// 增加客户端结构体
hThread *clientHeadNote, *clientEndNote;
hThread * addClient()
{
	hThread * ClientNote = new hThread();
	return ClientNote;
}
// 每个建立链接的客户端分配一个函数,每个线程运行一个这样的函数
void* ThreadClient(void*)
{
	if (clientEndNote == NULL)
	{
		printf("empty Link\n");
		return 0;
	}
	char revData[255];
	SOCKET sClient = clientEndNote->Client;
	printf("client logged in \n");
	while (1)
	{
		int ret = recv(sClient, revData, 255, 0);		// 接收数据
		if (ret > 0)
		{
			revData[ret] = 0x00;
			printf(revData);
			printf("\n");
			if (strcmp("log_out", revData) == 0)		// 新功能:加入客户端控制签退
			{
				char a[255];
				strcpy(a, "已注销\n");
				send(sClient, a, 255, 0);				// 反馈:向客户端sClienr发送“已注销”消息,发送长度为255个字节,1是指不阻塞。
				closesocket(sClient);
				printf("签退成功\n");
			}
			else
			{
				char a[255];
				strcpy(a, "服务器已收到消息\n");
				send(sClient, a, 255, 0);
			}
		}
	}
	closesocket(sClient);
}

int main(int argc, char* argv[])
{
	// 初始化WSA
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0)
	{
		return 0;
	}
	// 创建套接字:AF_INET:TCP/IP 协议,数据流,TCP 传输 
	SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (slisten == INVALID_SOCKET)
	{
		printf("socket error !");
		return 0;
	}

	//绑定IP和端口
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8888);								// 端口和客户端端口要相同,否则链接建立不上
	sin.sin_addr.S_un.S_addr = htonl(INADDR_ANY);			// 这个是any指的是不限制访问IP
//	char loa[16] = "192.168.15.189";						// 这是指定 IP发送数据 
//	sin.sin_addr.S_un.S_addr = inet_addr(loa);				// 加入指定 IP 


//	初始化slisten,加载上面代码设置的属性。
	if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)	// 把sin强制类型转换为sockadd,sin原来是sockaddr_in类型
	{
		printf("bind error !");
	}
	//开始监听
	if (listen(slisten, 5) == SOCKET_ERROR)
	{
		printf("listen error !");
		return 0;
	}
//	以上可以认为是建立服务器联系的固定格式


//这里是多线程服务器,接受多个客户端,可以对比单线程看看多了什么,哪些函数的位置调整了
	sockaddr_in remoteAddr;
	int nAddrlen = sizeof(remoteAddr);
	while (true)
	{
		printf("等待连接...\n");
		SOCKET sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
		if (sClient == INVALID_SOCKET)
		{
			printf("accept error !");
		}
		else
		{
			printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
			if (clientHeadNote == NULL)
			{
				clientHeadNote = addClient();
				clientEndNote = clientHeadNote;
			}
//			必须要先创建给end节点,然后再创建线程,因为线程的参数通过全局变量传入,而不是通过线程生成函数传入
//			原来线程参数除了 pthread_create()还能通过全局变量传参数
			hThread* clientnode = (hThread*)malloc(sizeof(hThread));								// 创建客户端结构体 
			clientnode->Client = sClient;
			clientnode->next = NULL;
			clientHeadNote->next = clientnode;
			clientEndNote = clientnode;
			clientnode->Thread = (pthread_t*)malloc(sizeof(pthread_t));								// 创建线程变量 
			clientnode->index = pthread_create(clientnode->Thread, NULL, ThreadClient, NULL);		// 创建线程并执行 
		}
	}
	printf("server is closing\n");
	hThread * p;
	while (clientHeadNote)  			 //释放占用的内存空间
	{
		p = clientHeadNote;
		clientHeadNote = clientHeadNote->next;
		delete p;
	}
	closesocket(slisten);
	WSACleanup();
	return 0;
}

这是单线程服务端 

#include 
#include 
#include 
#pragma comment(lib,"ws2_32.lib")
#pragma waring(disable :4996)
int main()
{
	WORD sockVersion = MAKEWORD(2,2);
	WSADATA wsaData;
	if(WSAStartup(sockVersion,&wsaData)!=0)
	{
		printf("版本与结构体不匹配\n");
		return 0;
	}
	SOCKET slisten = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(slisten==INVALID_SOCKET)
	{
		printf("socket error\n");
		return 0;
	}
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8888);
	sin.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	if(bind(slisten,(LPSOCKADDR)&sin,sizeof(sin))==SOCKET_ERROR)
	{
		printf("bind error\n");
	}
	if(listen(slisten,5)==SOCKET_ERROR)
	{
		printf("listen error\n");
		return 0;
	}
	sockaddr_in remoteAddr;
	int nAddrlen=sizeof(remoteAddr);
	char rev[255];
	char sen[244];
	strcpy(sen,"okk\n");
	printf("等待连接\n");
	SOCKET sClient = accept(slisten,(SOCKADDR*)&remoteAddr,&nAddrlen);
	if(sClient==INVALID_SOCKET)
	{
		printf("accpet error\n");
	}

	int ret=0;
	while(true)
	{
		ret=0;
		ret =recv(sClient,rev,255,0);
		if(ret>0)
		{
			printf("recieve\n");
			printf("ret = %d\n",ret);
			printf("消息为:%s\n",rev);
			send(sClient,sen,255,0);			// 0 代表阻塞发送,缓存起来,按顺序一个一个发
			strcpy(rev,"");						// 清空旧数据 
		}
		Sleep(100);
	}
}

这是客户端

#include 
#include 
#include 
#pragma  comment(lib,"ws2_32.lib")
#pragma warning(disable : 4996)	
//格式和服务端一样
int main(int argc, char* argv[])
{
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0)
	{
		return 0;
	}
	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sclient == INVALID_SOCKET)
	{
		printf("无效的 socket !");
		return 0;
	}
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;	// 表示使用IPv4地址协议 https://blog.csdn.net/u012736362/article/details/130392547
	serAddr.sin_port = htons(8888);
	char loa[16] = "127.0.0.1";		// 对方IP地址。当服务器的电脑的IP , win建+r 输入cmd,打开命令行,输入再直接 ipconfig/all可查看IP。
	serAddr.sin_addr.S_un.S_addr = inet_addr(loa);// IP是读取目标机器的IP,由于一台电脑当服务器和客户端,127.0.0.1是自回路,自己发送给自己


//链接发消息
	while(connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
		//主动连接服务器
		printf("连接错误 !\n");
		Sleep(1000);
	}
	puts("连接成功!!");
	puts("你现在可以向服务器发送信息:");
	char sendData[100];
	char recData[255];
	while (1)
	{
		scanf("%s",sendData);
		send(sclient, sendData, strlen(sendData), 0);		// 发送消息 0 代表阻塞 1 代表不阻塞 
		int ret = recv(sclient, recData, 255, 0);			// 接收消息 0 代表不执行这句,下面的都不执行 
		if (ret > 0)
		{
			printf(recData);
		}
	}
	closesocket(sclient);
	WSACleanup();
	return 0;
}

 

你可能感兴趣的:(tcp/ip,c语言,网络协议)