实现两(多)台windows电脑使用TCP套接字通信(亲测简单有效)

目录

一、前言

二、详细步骤

1.两台电脑连网(同一WIFF)

2.断开网络防火墙

3.获取网络IP地址

 4.修改客户端绑定的IP地址

5.连接测试 

三、源码

1.服务端代码:

2.客户端代码:


一、前言

        我们在学习完windows网络编程后,我想大多数人心底一直都有一个问题。

        网络编程就是实现多台电脑进行通信的,可我们自己在学习测试的时候,服务器端和客户端都是自己的电脑进行通信(此时ip地址为本机127.0.0),而这样的学习效果没有那么显著,没有发挥真正的网络通信的功能。

        那下面就跟着我的步骤:只需要准备两台电脑,服务端和客户端源代码即可实现。

二、详细步骤

1.两台电脑连网(同一WIFF)

        我们的第一步骤就是保证两台电脑连上同一个WIFF,手机热点也可以(座机情况不考虑,需要额外步骤)。

2.断开网络防火墙

        这一步很重要,我们需要断开自己电脑的防火墙(保护机制),要不然不能实现电脑通信。

3.获取网络IP地址

        (注意:这一步骤需要获取的是服务端的IP地址。)第一步: WIN + R , 输入cmd,回车。

        第二步:输入 ipconfig,获取连接无线网 WIFF 的 IPv4 地址。

 4.修改客户端绑定的IP地址

        在上一个步骤获取网络ip地址后,我的是 192.168.0.101,修改客户端代码绑定的IP地址。

5.连接测试 

        我们需要将准备的两台电脑一台电脑作为服务端,另一台(客户端可以有多个,多台电脑也可以)作为客户端,分别启动项目,连接测试。        

        这里我用的是我写的的项目代码,可以参考多线程网络实战之仿qq群聊的服务器和客户端

我这里测试的仿照qq的通过服务器,多个客户端通过通信。测试结果如下:

服务端:        

客户端:

三、源码

        这里你可以用自己的代码最好,我的代码有些复杂,不推荐,而且运行需要用开发者平台打开,参考文章:多线程网络实战之仿qq群聊的服务器和客户端

1.服务端代码:

        如下所示:

// 1. 对于每一个上线的客户端,服务端会起一个线程去维护
// 2. 将受到的消息转发给全部的客户端
// 3. 当某个客户端断开(下线),需要处理断开的链接。怎么处理呢?
#include <stdio.h>
#include <windows.h>
#include <process.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")


#define MAX_CLEN 256   
#define MAX_BUF_SIZE 1024

SOCKET clnSockets[MAX_CLEN]; // 所有的连接客户端的socket
int clnCnt = 0;   // 客户端连接的个数

HANDLE hMutex; 

// 将收到的消息转发给所有客户端
void SendMsg(char* msg, int len)
{
	int i;
	WaitForSingleObject(hMutex, INFINITE);
	for (i = 0; i < clnCnt; i++)
	{
		send(clnSockets[i], msg, len, 0);
	}
	ReleaseMutex(hMutex);
}

// 处理消息, 收发消息
unsigned WINAPI handleCln(void *arg)
{
	SOCKET hClnSock = *((SOCKET *)arg);
	int iLen = 0;
	char recvBuff[MAX_BUF_SIZE] = { 0 };
	
	while (1)
	{
		// iLen 成功时返回接收的字节数(收到EOF时为0),失败时返回SOCKETERROR。
		iLen = recv(hClnSock, recvBuff, MAX_BUF_SIZE, 0);
		// 
		if (iLen >= 0)
		{
			// 将收到的消息转发给所有客户端
			SendMsg(recvBuff,iLen);
		}
		else
		{
			break;
		}
	}

	printf("此时连接的客户端数量 = %d\n", clnCnt);
	WaitForSingleObject(hMutex, INFINITE);
	for (int i = 0; i < clnCnt; i++)
	{
		// 找到哪个连接下线的,移除这个连接
		if (hClnSock == clnSockets[i])
		{
			while (i++ < clnCnt)
			{
				clnSockets[i] = clnSockets[i + 1];
			}

			break;
		}
	}
	// 断开连接减 1 
	clnCnt--;
	printf("断开连接后连接的客户端数量 = %d\n", clnCnt);
	ReleaseMutex(hMutex);
	// 断开连接
	closesocket(hClnSock);
	return 0;
}

int main(int argc, char* argv[])
{	
	printf("this is Server\n");
	//0. 初始化网络
#if 1
// 0 初始化网络库
// 初始化库
	WSADATA wsaData;
	int stu = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (stu != 0) {
		std::cout << "WSAStartup 错误:" << stu << std::endl;
		return 0;
	}
#endif
	HANDLE hThread;

	// 1.  创建一个互斥对象
	hMutex = CreateMutex(NULL, false, NULL);

	// 2. socket 创建套接字
	SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

	if (sockSrv == INVALID_SOCKET)
	{
		std::cout << "socket failed!" << GetLastError() << std::endl;
		WSACleanup(); //释放Winsock库资源
		return 1;
	}

	// 3 bind 绑定套接字
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);   // 地址 IP地址any
	addrSrv.sin_family = AF_INET;    // ipv4协议
	addrSrv.sin_port = htons(6000);  // 端口号
	if ( SOCKET_ERROR == bind(sockSrv, (sockaddr*)&addrSrv, sizeof(SOCKADDR)))
	{
		std::cout << "bind failed!" << GetLastError() << std::endl;
		WSACleanup(); //释放Winsock库资源
		return 1;
	}
	// 4. 监听
	if (listen(sockSrv, 5) == SOCKET_ERROR) // 5 是指最大的监听数目,执行到listen
	{
		printf("listen error = %d\n", GetLastError());
		return -1;
	}
	
	// 5
	SOCKADDR_IN addrCli;
	int len = sizeof(SOCKADDR);

	while (true)
	{
		// 接受客户端的连接
		SOCKET sockCon = accept(sockSrv, (sockaddr*)&addrCli, &len);

		// 全局变量要加锁
		WaitForSingleObject(hMutex, INFINITE);
		// 将连接放到数组里面
		clnSockets[clnCnt++] = sockCon;
		// 解锁
		ReleaseMutex(hMutex);

		// 每接收一个客户端的连接,都安排一个线程去维护
		hThread = (HANDLE)_beginthreadex(NULL, 0, &handleCln, (void*)&sockCon, 0, NULL);

		printf("Connect client IP = %s\n, Num = %d \n", inet_ntoa(addrCli.sin_addr), clnCnt);
	}

	closesocket(sockSrv);
	CloseHandle(hMutex);
	WSACleanup();
	return 0;
}
2.客户端代码:

        如下所示:

// 客户端做的事情:
//1 请求连接上线,
//2 发消息
//3 客户端等待服务端的消息
//4 等待用户自己的关闭(下线)
#include <stdio.h>
#include <windows.h>
#include <process.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")

#define NAME_SIZE 256   
#define MAX_BUF_SIZE 1024

char szName[NAME_SIZE] = "[DEFAULT]"; //  默认的昵称
char szMsg[MAX_BUF_SIZE];    // 收发数据的大小



unsigned WINAPI SendMsg(void* arg)
{
	SOCKET hClnSock = *((SOCKET*)arg);
	char szNameMsg[NAME_SIZE + MAX_BUF_SIZE] = { 0 };   // 昵称和消息

	while (1)
	{
		memset(szMsg, 0, MAX_BUF_SIZE);
		// 阻塞这一句,等待控制台的消息
		//fgets(szMsg, MAX_BUF_SIZE, stdin);
		std::cin >> szMsg;
		if (!strcmp(szMsg, "Q\n") || !strcmp(szMsg, "q\n"))
		{
			// 处理下线
			closesocket(hClnSock);
			exit(0);
		}

		// 拼接  名字和字符串一起发送
		sprintf_s(szNameMsg, "%s %s", szName, szMsg);
		send(hClnSock, szNameMsg, strlen(szNameMsg) + 1, 0);

	}
}

unsigned WINAPI RecvMsg(void* arg)
{
	SOCKET hClnSock = *((SOCKET*)arg);
	char szNameMsg[NAME_SIZE + MAX_BUF_SIZE] = { 0 };   // 昵称和消息
	int len;
	while (1)
	{
		
		len = recv(hClnSock, szNameMsg, sizeof(szNameMsg), 0);
		if (len <= 0)
		{
			break;
			return -2;
		}
		szNameMsg[len] = 0;
		std::cout << szNameMsg << std::endl;
		// fputs(szNameMsg, stdout);


	}
	
}
int main(int argc, char* argv[])
{
	if (argc != 2)
	{
		printf("必须输入两个参数,包括昵称\n");
		printf("例如: WXS\n");
		system("pause");
		return -1;
	}
	sprintf_s(szName, "[%s]", argv[1]);
	printf("this is Client");
	//0. 初始化网络
#if 1
// 0 初始化网络库
// 初始化库
	WSADATA wsaData;
	int stu = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (stu != 0) {
		std::cout << "WSAStartup 错误:" << stu << std::endl;
		return 0;
	}
#endif
	
	// 定义两个线程 
	HANDLE hSendThread, hRecvThread;

	// 1. 建立 socket
	SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);

	if (sockCli == INVALID_SOCKET)
	{
		std::cout << "socket failed!" << GetLastError() << std::endl;
		WSACleanup(); //释放Winsock库资源
		return 1;
	}

	// 2, 配置IP地址 和 端口号
	SOCKADDR_IN addrSrv;
	addrSrv.sin_family = AF_INET;    // ipv4协议
	addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.7"); // 地址 IP地址any
	addrSrv.sin_port = htons(6000);  // 端口号

	// 3. 连接服务器
	int res = connect(sockCli, (sockaddr*)&addrSrv, sizeof(sockaddr));

	// 4. 发送服务器消息,启动线程
	hSendThread = (HANDLE)_beginthreadex(NULL, 0, &SendMsg, (void*)&sockCli, 0, NULL);
	// 5. 等待
	hRecvThread = (HANDLE)_beginthreadex(NULL, 0, &RecvMsg, (void*)&sockCli, 0, NULL);

	WaitForSingleObject(hSendThread,INFINITE);
	WaitForSingleObject(hRecvThread, INFINITE);

	closesocket(sockCli);
	WSACleanup();

	return 0;
}

以下是用C语言编写的利用socket实现TCP通讯的示例代码: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 int main(int argc, char *argv[]) { int sock; struct sockaddr_in serv_addr; char message[BUF_SIZE]; int str_len; if (argc != 3) { printf("Usage: %s <IP> <port>\n", argv[0]); exit(1); } sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("socket() error"); exit(1); } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(argv[1]); serv_addr.sin_port = htons(atoi(argv[2])); if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) { perror("connect() error"); exit(1); } else { puts("Connected..........."); } while (1) { fputs("Input message(Q to quit): ", stdout); fgets(message, BUF_SIZE, stdin); if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) { break; } str_len = write(sock, message, strlen(message)); int recv_len = 0; while (recv_len < str_len) { int len = read(sock, &message[recv_len], BUF_SIZE-1); if (len == -1) { perror("read() error"); exit(1); } recv_len += len; } message[recv_len] = '\0'; printf("Message from server: %s", message); } close(sock); return 0; } ``` 这是一个客户端的示例程序,它会连接到指定IP和端口的服务器,并与服务器上的另一个客户端进行通信。你需要将上面的代码保存为一个 C 文件(例如 client.c),并使用以下命令进行编译: ``` gcc -o client client.c ``` 然后运行客户端程序: ``` ./client <IP> <port> ``` 其中,`<IP>`和`<port>`是要连接的服务器的IP地址和端口号。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大卫的纯爱战士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值