C++socket编程学习总结(2)(发送信息send函数、recv函数与多线程实现多个用户同时连接)

今天接着昨天的学习来,昨天我复习了简单的TCP服务器的创建、绑定端口号、监听并接收信息,还有大端(网络字节流)小端(X86架构CPU使用的字节流)的概念。

今天复习发送信息send函数、recv函数与多线程实现多个用户同时连接。send函数用于发送一段数据,recv函数用于接收数据。

如下所示:

char buf[1024];//接收信息的最大长度,记位buf
	memset(buf, 1024, 0);
	while(true)
	{
		int recvlen = recv(client, buf, sizeof(buf) - 1, 0);//windows没有read函数,linux才有
		
		if (recvlen <= 0)break;//没有收到
		buf[recvlen] = '\0';
		if (strstr(buf, "q") != NULL)//按q退出
		{
			char re[] = "quit success!!!\n";
			send(client, re, strlen(re) + 1, 0);//加1是因为还有\0
			break;
		}
		int sendlen = send(client, "ok\n", 3, 0);//linux可以使用write
		cout << "RECEIVE:" << buf << endl;
		cout << "len:" << recvlen << endl;
		//len是接收数据的实际大小,len<=buf长度(这里是1024)
	}

 与昨天的代码连在一块这才是完整的代码,功能比较简单,当用户键入q时候退出连接但不知道为什么只能一次读取一个字符,盲猜是unicode编码问题,使得自动添加\0:

这是完整代码:

#include <iostream>
#include<thread>
#include<ws2tcpip.h>
#include<Windows.h>
#include<string>
#include<string.h>

using namespace std;

int main(int argc, char* argv[])
{
	//初始化动态链接库
	WSADATA ws;
	WSAStartup(MAKEWORD(2, 2), &ws);//22是版本号,加载动态链接库
	int sock = socket(AF_INET, SOCK_STREAM, 0);//AF_INET指明调用TCP/IP协议,SOCK_STREAM是TCP的协议(相对于UDP来讲)
	cout << sock << endl;//打印句柄id,失败返回负值
	//失败提示
	if (sock == -1)
	{
		cout << "create socket failed!" << endl;
		return -1;
	}

	//测试端口号
	unsigned short port = 8080;
	if (argc > 1)
	{
		port = atoi(argv[1]);
	}

	//创建TCP相关的结构体
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;//使用TCP
	saddr.sin_port = htons(port);//本地字节序转网络字节序
	//X86架构是小端的而网络字节流是大端的,
	//Linux不一定,小型linux使用的也是和网路字节序一样的话转换也只是一个空的宏,
	//这时候会可有可无,但考虑兼容性要求建议加上
	saddr.sin_addr.s_addr = htonl(0);//这里可以指定网卡,0是任意的意思

	if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0)//绑定端口号到上面创建的socket,并判断是否成功
	{
		cout << "bind port " << port << " failed!" << endl;
		return -2;
	}
	else
	{
		cout << "bind port " << port << " success!" << endl;
	}

	listen(sock, 10);//监听,接受连接;10是列表大小,套接字接收队列的最大大小 
	//accept每调用一次队列就会减少一个

	sockaddr_in caddr;
	socklen_t len = sizeof(caddr);
	int client = accept(sock, (sockaddr*)&caddr, &len);//取信息
	cout << client << endl;
	char* ip = inet_ntoa(caddr.sin_addr);
	unsigned short cport = ntohs(caddr.sin_port);//网络字节序转本地字节序
	cout << "client ip is " << ip << " port is " << cport << endl;
	char buf[1024];//接收信息的最大长度,记位buf
	memset(buf, 1024, 0);
	while(true)
	{
		int recvlen = recv(client, buf, sizeof(buf) - 1, 0);//windows没有read函数,linux才有
		
		if (recvlen <= 0)break;//没有收到
		buf[recvlen] = '\0';
		if (strstr(buf, "q") != NULL)//按q退出
		{
			char re[] = "quit success!!!\n";
			send(client, re, strlen(re) + 1, 0);//加1是因为还有\0
			break;
		}
		int sendlen = send(client, "ok\n", 3, 0);//linux可以使用write
		cout << "RECEIVE:" << buf << endl;
		cout << "len:" << recvlen << endl;
		//len是接收数据的实际大小,len<=buf长度(这里是1024)
	}


	closesocket(client);//关闭连接

	return 0;
}

当是这个时候只能有一个用户接入,这时候得用多线程来解决问题,我使用了C++11的标准库thread。

先定义一个类tcpshread,然后每有一个用户连接那就new一个Tcpthread,如下:

class tcpthread
{
public:
	void Main()
	{
		char buf[1024] = { 0 };//接收信息的最大长度,记位buf
		while (true)
		{
			int recvlen = recv(client, buf, sizeof(buf) - 1, 0);//windows没有read函数,linux才有
			if (recvlen <= 0)break;//没有收到
			if (strstr(buf, "q") != NULL)//按q退出
			{
				char re[] = "quit success!!!\n";
				send(client, re, strlen(re) + 1, 0);//加1是因为还有\0
				break;
			}
			int sendlen = send(client, "ok\n", 3, 0);//linux可以使用write
			cout << "receive:" << buf << endl;
			//len是接收数据的实际大小,len<=buf长度(这里是1024)
		}


		closesocket(client);//关闭连接

	}
	int client = 0;
};

这是完整代码:

#include <iostream>
#include<thread>
#include<ws2tcpip.h>
#include<Windows.h>
#include<string>
#include<string.h>

using namespace std;

class tcpthread
{
public:
	void Main()
	{
		char buf[1024] = { 0 };//接收信息的最大长度,记位buf
		while (true)
		{
			int recvlen = recv(client, buf, sizeof(buf) - 1, 0);//windows没有read函数,linux才有
			if (recvlen <= 0)break;//没有收到
			if (strstr(buf, "q") != NULL)//按q退出
			{
				char re[] = "quit success!!!\n";
				send(client, re, strlen(re) + 1, 0);//加1是因为还有\0
				break;
			}
			int sendlen = send(client, "ok\n", 3, 0);//linux可以使用write
			cout << "receive:" << buf << endl;
			//len是接收数据的实际大小,len<=buf长度(这里是1024)
		}


		closesocket(client);//关闭连接

	}
	int client = 0;
};

int main(int argc, char* argv[])
{
	//初始化动态链接库
	WSADATA ws;
	WSAStartup(MAKEWORD(2, 2), &ws);//22是版本号,加载动态链接库
	int sock = socket(AF_INET, SOCK_STREAM, 0);//AF_INET指明调用TCP/IP协议,SOCK_STREAM是TCP的协议(相对于UDP来讲)
	cout << sock << endl;//打印句柄id,失败返回负值
	//失败提示
	if (sock == -1)
	{
		cout << "create socket failed!" << endl;
		return -1;
	}

	//测试端口号
	unsigned short port = 8080;
	if (argc > 1)
	{
		port = atoi(argv[1]);
	}

	//创建TCP相关的结构体
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;//使用TCP
	saddr.sin_port = htons(port);//本地字节序转网络字节序
	//X86架构是小端的而网络字节流是大端的,
	//Linux不一定,小型linux使用的也是和网路字节序一样的话转换也只是一个空的宏,
	//这时候会可有可无,但考虑兼容性要求建议加上
	saddr.sin_addr.s_addr = htonl(0);//这里可以指定网卡,0是任意的意思

	if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0)//绑定端口号到上面创建的socket,并判断是否成功
	{
		cout << "bind port " << port << " failed!" << endl;
		return -2;
	}
	else
	{
		cout << "bind port " << port << " success!" << endl;
	}

	listen(sock, 10);//监听,接受连接;10是列表大小,套接字接收队列的最大大小 
	//accept每调用一次队列就会减少一个

	while(true)
	{
		sockaddr_in caddr;
		socklen_t len = sizeof(caddr);
		int client = accept(sock, (sockaddr*)&caddr, &len);//取信息
		if (client <= 0)break;

		cout << client << endl;
		char* ip = inet_ntoa(caddr.sin_addr);
		unsigned short cport = ntohs(caddr.sin_port);//网络字节序转本地字节序
		cout << "client ip is " << ip << " port is " << cport << endl;
		tcpthread *th = new tcpthread();
		th->client = client;
		thread sth(&tcpthread::Main, th);
		sth.detach();//释放主线程拥有的子线程的资源
	}
	closesocket(sock);

	return 0;
}

测试结果图:

扫描二维码关注公众号,回复: 10753745 查看本文章

END

发布了162 篇原创文章 · 获赞 38 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41938259/article/details/104916488