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;
}
测试结果图:
END
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2022-06-08 Django实战(2)——Django视图初步(多个页面,超链接至另一个网页,基本的文本显示)