accept 具有独占性【线程安全】; 即 多线程环境中用accept监听同一个socket,当就绪事件发生时;对于accept只有一个完成就绪状态;
select 具有共享性【线程不安全】:即 多线程环境中用select监听同一个socket,当就绪事件发生时;所有监听这个同一个socket的select就绪状态同时完成;并且可能造成不安全风险,比如其中一个线程close socket
// server.cpp
#include<iostream>
#include<vector>
#include<chrono>
#include<thread>
#include<memory>
#include<tuple>
#include<initializer_list>
#include"CWsaInit.h"
#pragma comment(lib,"ws2_32.lib")
using namespace std;
using namespace std::this_thread;
constexpr int maxpath = 256;
constexpr USHORT ServerPort = 8888;
WsaInit init;
//accept 与 select的多线程行为
//accept 具有独占性【线程安全】; 即 多线程环境中用accept监听同一个socket,当就绪事件发生时;对于accept只有一个完成就绪状态;
//select 具有共享性【线程不安全】:即 多线程环境中用select监听同一个socket,当就绪事件发生时;所有监听这个同一个socket的select就绪状态同时完成;并且可能造成不安全风险,比如其中一个线程close socket
void Process(SOCKET socklisten)
{
sockaddr_in localAddr;
socklen_t len = sizeof(localAddr);
getsockname(socklisten, reinterpret_cast<sockaddr*>(&localAddr), &len);
cout << "listenSock at atThread:" << socklisten << endl;
char buf[128]{};
inet_ntop(AF_INET, &localAddr.sin_addr, buf, sizeof(buf));
cout << buf << ": 端口:" << ntohs(localAddr.sin_port) << "本端信息atThread!" << endl;
sockaddr_in clientinfo;
len = sizeof(clientinfo);
std::this_thread::sleep_for(2s);
while (true)
{
fd_set fdread;
FD_ZERO(&fdread);
FD_SET(socklisten, &fdread);
auto nres = select(0, &fdread, nullptr, nullptr, nullptr);
if (SOCKET_ERROR == nres)
{
auto error = WSAGetLastError();
cout << "select error:" << error << endl;
exit(-1);
}
//closesocket(socklisten);//分线程共享进程的文件句柄表;与子进程不同 分线程并没有增加文件对象的引用计数 是风险行为
//cout << " Processthread closesocket" << endl;
cout << "select at Processthread" << endl;
SOCKET sockcfd = accept(socklisten, reinterpret_cast<sockaddr*>(&clientinfo), &len);
if (sockcfd == INVALID_SOCKET)
{
auto error = WSAGetLastError();
cout << "accept error:" << error << endl;
exit(-1);
}
cout << "accepted at Processthread:sockcfd=" << sockcfd << endl;
}
return;
}
int main()
{
char szName[maxpath]{};
if (gethostname(szName, sizeof(szName)) == SOCKET_ERROR)
{
auto err = WSAGetLastError();
}
cout << szName << endl;
SOCKET socklisten = socket(AF_INET, SOCK_STREAM, 0);
if (socklisten == INVALID_SOCKET)
exit(-1);
sockaddr_in localaddr;
localaddr.sin_family = AF_INET; //地址类型
localaddr.sin_port = htons(ServerPort); //端口号
if (inet_pton(AF_INET, "127.0.0.1", &localaddr.sin_addr) != 1) //IP地址
exit(-1);
if (bind(socklisten, reinterpret_cast<sockaddr*>(&localaddr), sizeof(sockaddr_in)) == SOCKET_ERROR)
exit(-1);
if (SOCKET_ERROR == listen(socklisten, 128))
exit(-1);
cout << "127.0.0.1:8888 :开启监听" << endl;
sockaddr_in localAddr;
socklen_t len = sizeof(localAddr);
getsockname(socklisten, reinterpret_cast<sockaddr*>(&localAddr), &len);
char buf[128]{};
inet_ntop(AF_INET, &localAddr.sin_addr, buf, sizeof(buf));
cout << buf << ": 端口:" << ntohs(localAddr.sin_port) << "本端信息" << endl;
sockaddr_in clientinfo;
len = sizeof(clientinfo);
cout << "listenSock at mainThread:" << socklisten << endl;
thread t(Process, socklisten);
std::this_thread::sleep_for(2s);
int nRespond = 0;
while (true)
{
fd_set fdread;
FD_ZERO(&fdread);
FD_SET(socklisten, &fdread);
auto nres=select(0, &fdread, nullptr, nullptr, nullptr);
if (SOCKET_ERROR == nres)
{
auto error = WSAGetLastError();
cout << "select error:" << error << endl;
exit(-1);
}
cout << "select at :mainthread" << endl;
SOCKET sockcfd = accept(socklisten, reinterpret_cast<sockaddr*>(&clientinfo), &len);
if (sockcfd == INVALID_SOCKET)
{
auto error = WSAGetLastError();
cout << "accept error:" << error << endl;
exit(-1);
}
cout << "accepted at mainthread:sockcfd=" << sockcfd << endl;
++nRespond;
if (nRespond >= 5)
break;
}
t.join();
return 0;
}
//客户端
#include<iostream>
#include<vector>
#include<chrono>
#include<thread>
#include<memory>
#include<tuple>
#include<initializer_list>
#include"CWsaInit.h"
#pragma comment(lib,"ws2_32.lib")
using namespace std;
using namespace std::this_thread;
constexpr int maxpath = 256;
constexpr USHORT ServerPort = 8888;
WsaInit init;
int main()
{
char szName[maxpath]{};
if (gethostname(szName, sizeof(szName)) == SOCKET_ERROR)
{
auto err = WSAGetLastError();
}
cout << szName << endl;
SOCKET sockclient = socket(AF_INET, SOCK_STREAM, 0);
if (sockclient == INVALID_SOCKET)
exit(-1);
sockaddr_in ServerAddr;
socklen_t len = sizeof(sockaddr_in);
ServerAddr.sin_family = AF_INET; //地址类型
ServerAddr.sin_port = htons(ServerPort); //端口号
if (inet_pton(AF_INET, "127.0.0.1", &ServerAddr.sin_addr) != 1) //IP地址
exit(-1);
int err=connect(sockclient, reinterpret_cast<sockaddr*>(&ServerAddr), len);
if (err == SOCKET_ERROR)
{
auto error = WSAGetLastError();
exit(-1);
}
sockaddr_in localAddr;
len = sizeof(sockaddr_in);
getsockname(sockclient, reinterpret_cast<sockaddr*>(&localAddr), &len);
char buf[128]{};
inet_ntop(AF_INET, &localAddr.sin_addr, buf, sizeof(buf));
cout << "客户端 sock "<<buf << ": 端口:" << ntohs(localAddr.sin_port) << "本端信息" << endl;
while (getchar()!='e')
{
}
return 0;
}