赞
踩
今天接着昨天的学习来,昨天我复习了简单的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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。