TCP/IP,UDP通信——C++实现

基于TCP(面向连接)的socket编程

服务器端程序:

  1. 创建套接字(socket)
  2. 将套接字绑定到一个本地地址和端口上(bind):目的是为了告诉客户端,服务器准备在那个ip地址哪个端口接受请求。
  3. 将套接字设置为监听模式,准备接收客户端请求(listen)
  4. 等待客户端请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。接收客户端请求后,就保存了客户端的IP和端口号。
  5. 用返回的套接字 ,与客户端进行通信(send/recv)
  6. 当不需要的时候,关闭套接字(closesocket)

客户端程序:

  1. 创建套接字(socket)
  2. 向服务器发出连接请求(connect)
  3. 和服务器端进行通信(send/recv)
  4. 关闭套接字(closesocket)

运行时,服务器端先启动。

基于UDP(面向无连接)的socket编程

服务器端(接收端)程序:

  1. 创建套接字(socket)
  2. 将套接字绑定到一个本地地址和端口上(bind):告诉客户端,服务器端在哪个端口哪个IP上等待数据。
  3. 等待接收数据(recvfrom)
  4. 关闭套接字(closescoket)

客户端(发送端)程序:

  1. 创建套接字(socket)
  2. 向服务器发送数据(sendto)
  3. 关闭套接字(closesocket)

运行时,服务器端(接收端)先启动。

以下例子,均是基于VS2010的win32控制台应用程序。

TCP/IP

TcpServer.cpp如下:属性中,链接里面,如要输入:ws2_32.lib

//#include <Windows.h>// 不能与#include <WinSock2.h>,同时存在。否则会出现函数重定义。
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 加载socket库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return 0;
	}

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

	// 绑定套接字
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);// 将u_long类型转换为网络字节序
	addrSrv.sin_family = AF_INET; // 地址族
	addrSrv.sin_port = htons(6000);// 用1024以上的端口号

	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	// listen监听
	listen(sockSrv, 5);// 等待链接的最大数,设为5

	// 接收客户端地址信息
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	while (1)
	{
		SOCKET sockConn =  accept(sockSrv, (SOCKADDR*)&addrClient, &len);//返回相对于这个链接的套接字
		char sendBuf[100];
		sprintf(sendBuf, "welcome %s to connect server!\n", 
			inet_ntoa(addrClient.sin_addr));
		send(sockConn,sendBuf, strlen(sendBuf)+1, 0);
		
		char recvBuf[100];
		recv(sockConn, recvBuf, 100, 0);

		printf("%s\n", recvBuf);
		closesocket(sockConn);
	}
	WSACleanup();
	system("pause");
	return 0;
}

TcpClient.cpp文件.属性中,链接里面,如要输入:ws2_32.lib

#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 加载socket库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return 0;
	}

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

	// 发送连接请求
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 本地回路地址. 地址为u_long类型,需要用inet_addr进行转换
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

        // 接收并发送消息,完成一次对话
 	char recvBuf[100];
 	recv(sockClient, recvBuf, 100, 0);
 	printf("%s\n", recvBuf);
 	send(sockClient, "this is client", sizeof("this is client")+1, 0);

        // 最后关闭套接字,清理套接字库
	closesocket(sockClient);
	WSACleanup();
	system("pause");
	return 0;
}

服务器处于等待状态,客户端可以开启多个,然后连接到服务器端。

猜想,是不是客户端和服务器端通信之后,可以像聊天一样,你一言我一语的进行交流呢? 那就把客户端 TcpClient.cpp 改一下循环试试,如下:

#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 加载socket库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return 0;
	}

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

	// 发送连接请求
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 本地回路地址. 地址为u_long类型,需要用inet_addr进行转换
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

// 	char recvBuf[100];
// 	recv(sockClient, recvBuf, 100, 0);
// 	printf("%s\n", recvBuf);
// 	send(sockClient, "this is client", sizeof("this is client")+1, 0);

	while (1)
	{
		char recvBuf[100];
		recv(sockClient, recvBuf, 100, 0);
		printf("server say : %s\n", recvBuf);
		send(sockClient, "this is client", sizeof("this is client")+1, 0);
	}

	closesocket(sockClient);
	WSACleanup();
	system("pause");
	return 0;
}

然后出现了如下问题:客户端会一直接收到服务器端的信息,但是服务器只接收一次客户端信息。可能的原因:服务器只连接一次。也就是对于一个IP,仅接受一次请求。 不能像聊天那样进行。如果需要聊天那样的话,是基于UDP通信,可以实现。

UDP

UdpServer.cpp, 属性中,链接里面,如要输入:ws2_32.lib

#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 加载socket库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return 0;
	}

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

	// 绑定
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	// 接收
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	char recvBuf[100];
	recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);
	printf("%s\n", recvBuf);
	
	closesocket(sockSrv);
	WSACleanup();

	system("pause");
	return 0;
}

UdpClient.cpp.属性中,链接里面,如要输入:ws2_32.lib

#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 加载socket库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return 0;
	}

	// 创建套接字
	SOCKET client = socket(AF_INET, SOCK_DGRAM, 0);

	// 发送
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	sendto(client, "this is client", sizeof("this is client"), 0, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));


	closesocket(client);
	WSACleanup();
	system("pause");
	return 0;
}

运行结果:

基于UDP的简单的聊天窗口

UdpServer.cpp, 属性中,链接里面,如要输入:ws2_32.lib

#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 加载socket库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return 0;
	}

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

	// 绑定
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	char recvBuf[100];
	char sendBuf[100];
	char tempBuf[100];

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	while (TRUE)
	{
		recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);// 先接收数据
		if ('q' == recvBuf[0])
		{
			sendto(sockSrv, "q", strlen("q")+1, 0, (SOCKADDR*)&addrClient, len);
			printf("Client Chat end!\n");
			//cout << "Client Chat end!" << endl;
			break;
		}
		sprintf(tempBuf, "%s say : %s", inet_ntoa(addrClient.sin_addr), recvBuf);
		//cout << inet_ntoa(addrClient.sin_addr) << " say: " << recvBuf << endl;
		printf("%s\n", tempBuf);
		//cout << tempBuf << endl;

		cout << "请输入内容:" << endl;// C++
		//cin >> sendBuf;
		gets(sendBuf);// 从键盘输入    // C标准输入
		sendto(sockSrv, sendBuf, strlen(sendBuf)+1, 0, (SOCKADDR*)&addrClient, len);
	}	
	
	closesocket(sockSrv);
	WSACleanup();

	system("pause");
	return 0;
}

UdpClient.cpp  文件。属性中,链接里面,如要输入:ws2_32.lib

#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 加载socket库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return 0;
	}

	// 创建套接字
	SOCKET client = socket(AF_INET, SOCK_DGRAM, 0);

	// 发送
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	char recvBuf[100];
	char sendBuf[100];
	char tempBuf[100];
	int len = sizeof(SOCKADDR);
	// 客户端先发送数据
	while (TRUE)
	{
		cout << "请输入内容:" << endl;
		gets(sendBuf);
		//cin >> sendBuf;
		sendto(client, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
		recvfrom(client, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&addrSrv, &len);
		if ('q' == recvBuf[0])
		{
			sendto(client, "q", sizeof("q")+1, 0, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
			//printf("Server Chat end!\n");
			cout <<"Server Chat end!" << endl;
			break;
		}
		sprintf(tempBuf, "%s say : %s", inet_ntoa(addrSrv.sin_addr), recvBuf);
		//cout << inet_ntoa(addrSrv.sin_addr) <<" say : "<< recvBuf << endl;
		//printf("%s\n", tempBuf);
		cout << tempBuf << endl;
	}
	
	closesocket(client);
	WSACleanup();
	system("pause");
	return 0;
}

运行结果:

当我把标准输入输出,直接换为 cin/cout的时候,就出现了神奇的现象,如下:可能哪里不对吧。

过程中,遇到的问题:

为什么获取客户端ip地址总是:204.204.204.204

解决方案:

bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));// 判断试一下,绑定是否正确。我这里把第二个参数,误写成了第一个参数,就会出现这个问题。

204换成16进制是0xcc,这是vc初始化堆栈的时候使用的数据。也就是accept没有成专功,所以里面没数据。accept接收失败,会返回-1.

发布了418 篇原创文章 · 获赞 156 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_34732729/article/details/105556610
今日推荐