功能
使用了C++ socket,thread等方面的知识,支持多机聊天。
实现原理
本来是C/S架构,想通过一个服务器端进行信息转发,后来发现自己写一个服务器实在不太现实。所以把发送和接收的功能打包进一套程序,多机通过局域网内互联,实现聊天功能。
架构
main
{
init socket
recv info//接收消息
while true:
if want to chat:
type 'c'
chat//发送消息
if want to exit:
type 'esc'
break;
}
Socket
接收消息
WSADATA wsd2;
//接收消息服务器套接字
SOCKET Sserver;
//接收消息服务器套接字地址
SOCKADDR_IN addrServ;
//接收消息发送方远程客户端套接字
SOCKET Sclient;
//接收消息发送方远程客户端套接字地址
sockaddr_in addrClient;
//接收消息发送方远程客户端套接字地址长度
int addrClientLen;
//初始化套结字动态库
if (WSAStartup(MAKEWORD(2, 2), &wsd2) != 0)
{
cout << "WSAStartup failed!" << endl;
return 1;
}
cout << "Socket Init..." << endl;
//创建套接字
Sserver = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP协议
if (Sserver == INVALID_SOCKET)
{
cout << "Failed Socket" << endl;
WSACleanup();
return -1;
}
cout << "Input Your Port" << endl;
int port;
cin >> port;
//设置服务器套接字
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons((short)port);
//addrServ.sin_addr.S_un.S_addr = INADDR_ANY;//0.0.0.0本机ip地址
addrServ.sin_addr.s_addr = inet_addr("127.0.0.1");//"127.0.0.1"
cout << "Service Socket Done..." << endl;
int addrServLen = sizeof(addrServ);
//绑定套接字
int ret = ::bind(Sserver, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));//socket里的bind和std::bind重复出错
if (ret == SOCKET_ERROR)
{
cout << "bind failed" << endl;
closesocket(Sserver);
WSACleanup();
return -1;
}
cout << "Bind Done..." << endl;
ret = listen(Sserver, 10);//最大支持十个客户端连接
if (ret == SOCKET_ERROR)
{
cout << "listen failed" << endl;
closesocket(Sserver);
WSACleanup();
return -1;
}
cout << "Listen Done..." << endl;
addrClientLen = sizeof(addrClient);
// ready
Sleep(1000);
cout << "Open Service And Recv..." << endl;
while (true)
{
cout << "Waiting Client..." << endl;
int ch;
if (_kbhit())
{
ch = _getch();
cout << "You just type" << ch << endl;
if (ch == 113)//q
{
return 0;
}
}
//接收请求
Sclient = accept(Sserver, (sockaddr FAR*)&addrClient, &addrClientLen);
if (Sclient == INVALID_SOCKET)
{
cout << "failed socket" << endl;
WSACleanup();
return -1;
}
//接收数据
const int BUFF_SIZE = 1024;
char buf[BUFF_SIZE];
char *reply = "Done!";
while (true)
{
ZeroMemory(buf, BUFF_SIZE);//清空置零
int ret = recv(Sclient, buf, BUFF_SIZE, 0);
if (ret == SOCKET_ERROR)
{
cout << "recv failed" << endl;
break;
//return -1;
}
getpeername(Sclient, (struct sockaddr *)&addrClient, &addrClientLen); //查看链接对端的地址
cout << "\n" << endl;
//cout << "Friend from " << inet_ntoa(addrClient.sin_addr) << ":" << ntohs(addrClient.sin_port) << ":" << buf << endl;
cout << "Friend:"<< buf << endl;
getsockname(Sserver, (struct sockaddr *)&addrServ, &addrServLen);//查看链接本端的地址
cout << "\n" << endl;
//cout << "You are" << inet_ntoa(addrServ.sin_addr) << ":" << ntohs(addrServ.sin_port) << endl;
//cout << "You:" << reply << endl;
send(Sclient, reply, strlen(reply), 0);
}
}
发送消息
发送消息主要需要设置好对方的地址和端口
//初始化套接字库
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
cout << "WSAStartup failed" << endl;
return -1;
}
cout << "Chat Socket Init..." << endl;
Sremotehost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Sremotehost == INVALID_SOCKET)
{
cout << "Chat Socket Failed" << endl;
WSACleanup();
return -1;
}
cout << "Service Socket Done..." << endl;
addrRemoteServ.sin_family = AF_INET;
addrRemoteServ.sin_port = htons((short)atoi(port));//4999atoi(port)
addrRemoteServ.sin_addr.s_addr = inet_addr(ip);//"127.0.0.1"
addrRemoteServLen = sizeof(addrRemoteServ);
//连接服务器
int ret = connect(Sremotehost, (LPSOCKADDR)&addrRemoteServ, addrRemoteServLen);
if (ret == SOCKET_ERROR)
{
cout << "Connect Failed" << endl;
cout << ::WSAGetLastError() << endl;
closesocket(Sremotehost);
WSACleanup();
return -1;
}
cout << "Connect Done..." << endl;
return 0;
小结
C++ windows下使用socket
#include<winsock2.h>
#pragma comment(lib, "ws2_32.lib") //加载动态库
getpeername
当链接建立后,获得链接对端地址
//Socket,sockaddr_in,int Len = sizeof(sockaddr)
getpeername(Sclient, (struct sockaddr *)&addrClient, &addrClientLen); //获取connfd表示的连接上的对端地址
cout << "Friend from " << inet_ntoa(addrClient.sin_addr) << ":" << ntohs(addrClient.sin_port) << ":" << buf << endl;
getsockname
当链接建立后,获得本端地址
getsockname(Sserver, (struct sockaddr *)&addrServ, &addrServLen);
cout << "You are" << inet_ntoa(addrServ.sin_addr) << ":" << ntohs(addrServ.sin_port) << endl;
Socket bind函数与std bind函数重名
C++ 11后std中添加了bind函数,当使用using namespace std的时候会产生重名错误,可以使用::bind来使用socket中的bind。
WSAStartup
初始化套接字库,其只初始化一次,如果多次调用,会在计数器上加一,当调用结束使用WSACleanup()时,调用几次需要Clean几次,每次Clean会使计数器减一,减到0时释放资源。
Socket过程中的错误
可以通过 WSAGetLastError 函数查看具体错误,返回值为int型,其对应的错误在winsock包里。
cout << ::WSAGetLastError() << endl;
如果需要查询可以在包中搜索 10000,因为错误代码是从10000开始的:
#define WSABASEERR 10000
/*
* Windows Sockets definitions of regular Microsoft C error constants
*/
#define WSAEINTR (WSABASEERR+4)
#define WSAEBADF (WSABASEERR+9)
#define WSAEACCES (WSABASEERR+13)
#define WSAEFAULT (WSABASEERR+14)
#define WSAEINVAL (WSABASEERR+22)
#define WSAEMFILE (WSABASEERR+24)
/*
* Windows Sockets definitions of regular Berkeley error constants
*/
#define WSAEWOULDBLOCK (WSABASEERR+35)
#define WSAEINPROGRESS (WSABASEERR+36)
#define WSAEALREADY (WSABASEERR+37)
#define WSAENOTSOCK (WSABASEERR+38)
#define WSAEDESTADDRREQ (WSABASEERR+39)
#define WSAEMSGSIZE (WSABASEERR+40)
#define WSAEPROTOTYPE (WSABASEERR+41)
#define WSAENOPROTOOPT (WSABASEERR+42)
#define WSAEPROTONOSUPPORT (WSABASEERR+43)
#define WSAESOCKTNOSUPPORT (WSABASEERR+44)
#define WSAEOPNOTSUPP (WSABASEERR+45)
#define WSAEPFNOSUPPORT (WSABASEERR+46)
#define WSAEAFNOSUPPORT (WSABASEERR+47)
#define WSAEADDRINUSE (WSABASEERR+48)
#define WSAEADDRNOTAVAIL (WSABASEERR+49)
#define WSAENETDOWN (WSABASEERR+50)
#define WSAENETUNREACH (WSABASEERR+51)
#define WSAENETRESET (WSABASEERR+52)
#define WSAECONNABORTED (WSABASEERR+53)
#define WSAECONNRESET (WSABASEERR+54)
#define WSAENOBUFS (WSABASEERR+55)
#define WSAEISCONN (WSABASEERR+56)
#define WSAENOTCONN (WSABASEERR+57)
#define WSAESHUTDOWN (WSABASEERR+58)
#define WSAETOOMANYREFS (WSABASEERR+59)
#define WSAETIMEDOUT (WSABASEERR+60)
#define WSAECONNREFUSED (WSABASEERR+61)
#define WSAELOOP (WSABASEERR+62)
#define WSAENAMETOOLONG (WSABASEERR+63)
#define WSAEHOSTDOWN (WSABASEERR+64)
#define WSAEHOSTUNREACH (WSABASEERR+65)
#define WSAENOTEMPTY (WSABASEERR+66)
#define WSAEPROCLIM (WSABASEERR+67)
#define WSAEUSERS (WSABASEERR+68)
#define WSAEDQUOT (WSABASEERR+69)
#define WSAESTALE (WSABASEERR+70)
#define WSAEREMOTE (WSABASEERR+71)
/*
* Extended Windows Sockets error constant definitions
*/
#define WSASYSNOTREADY (WSABASEERR+91)
#define WSAVERNOTSUPPORTED (WSABASEERR+92)
#define WSANOTINITIALISED (WSABASEERR+93)
#define WSAEDISCON (WSABASEERR+101)
#define WSAENOMORE (WSABASEERR+102)
#define WSAECANCELLED (WSABASEERR+103)
#define WSAEINVALIDPROCTABLE (WSABASEERR+104)
#define WSAEINVALIDPROVIDER (WSABASEERR+105)
#define WSAEPROVIDERFAILEDINIT (WSABASEERR+106)
#define WSASYSCALLFAILURE (WSABASEERR+107)
#define WSASERVICE_NOT_FOUND (WSABASEERR+108)
#define WSATYPE_NOT_FOUND (WSABASEERR+109)
#define WSA_E_NO_MORE (WSABASEERR+110)
#define WSA_E_CANCELLED (WSABASEERR+111)
#define WSAEREFUSED (WSABASEERR+112)
/*
* Error return codes from gethostbyname() and gethostbyaddr()
* (when using the resolver). Note that these errors are
* retrieved via WSAGetLastError() and must therefore follow
* the rules for avoiding clashes with error numbers from
* specific implementations or language run-time systems.
* For this reason the codes are based at WSABASEERR+1001.
* Note also that [WSA]NO_ADDRESS is defined only for
* compatibility purposes.
*/
/* Authoritative Answer: Host not found */
#define WSAHOST_NOT_FOUND (WSABASEERR+1001)
/* Non-Authoritative: Host not found, or SERVERFAIL */
#define WSATRY_AGAIN (WSABASEERR+1002)
/* Non-recoverable errors, FORMERR, REFUSED, NOTIMP */
#define WSANO_RECOVERY (WSABASEERR+1003)
/* Valid name, no data record of requested type */
#define WSANO_DATA (WSABASEERR+1004)
/*
* Define QOS related error return codes
*
*/
#define WSA_QOS_RECEIVERS (WSABASEERR + 1005)
/* at least one Reserve has arrived */
#define WSA_QOS_SENDERS (WSABASEERR + 1006)
/* at least one Path has arrived */
#define WSA_QOS_NO_SENDERS (WSABASEERR + 1007)
/* there are no senders */
#define WSA_QOS_NO_RECEIVERS (WSABASEERR + 1008)
/* there are no receivers */
#define WSA_QOS_REQUEST_CONFIRMED (WSABASEERR + 1009)
/* Reserve has been confirmed */
#define WSA_QOS_ADMISSION_FAILURE (WSABASEERR + 1010)
/* error due to lack of resources */
#define WSA_QOS_POLICY_FAILURE (WSABASEERR + 1011)
/* rejected for administrative reasons - bad credentials */
#define WSA_QOS_BAD_STYLE (WSABASEERR + 1012)
/* unknown or conflicting style */
#define WSA_QOS_BAD_OBJECT (WSABASEERR + 1013)
/* problem with some part of the filterspec or providerspecific
* buffer in general */
#define WSA_QOS_TRAFFIC_CTRL_ERROR (WSABASEERR + 1014)
/* problem with some part of the flowspec */
#define WSA_QOS_GENERIC_ERROR (WSABASEERR + 1015)
/* general error */
#define WSA_QOS_ESERVICETYPE (WSABASEERR + 1016)
/* invalid service type in flowspec */
#define WSA_QOS_EFLOWSPEC (WSABASEERR + 1017)
/* invalid flowspec */
#define WSA_QOS_EPROVSPECBUF (WSABASEERR + 1018)
/* invalid provider specific buffer */
#define WSA_QOS_EFILTERSTYLE (WSABASEERR + 1019)
/* invalid filter style */
#define WSA_QOS_EFILTERTYPE (WSABASEERR + 1020)
/* invalid filter type */
#define WSA_QOS_EFILTERCOUNT (WSABASEERR + 1021)
/* incorrect number of filters */
#define WSA_QOS_EOBJLENGTH (WSABASEERR + 1022)
/* invalid object length */
#define WSA_QOS_EFLOWCOUNT (WSABASEERR + 1023)
/* incorrect number of flows */
#define WSA_QOS_EUNKOWNPSOBJ (WSABASEERR + 1024)
/* unknown object in provider specific buffer */
#define WSA_QOS_EPOLICYOBJ (WSABASEERR + 1025)
/* invalid policy object in provider specific buffer */
#define WSA_QOS_EFLOWDESC (WSABASEERR + 1026)
/* invalid flow descriptor in the list */
#define WSA_QOS_EPSFLOWSPEC (WSABASEERR + 1027)
/* inconsistent flow spec in provider specific buffer */
#define WSA_QOS_EPSFILTERSPEC (WSABASEERR + 1028)
/* invalid filter spec in provider specific buffer */
#define WSA_QOS_ESDMODEOBJ (WSABASEERR + 1029)
/* invalid shape discard mode object in provider specific buffer */
#define WSA_QOS_ESHAPERATEOBJ (WSABASEERR + 1030)
/* invalid shaping rate object in provider specific buffer */
#define WSA_QOS_RESERVED_PETYPE (WSABASEERR + 1031)
/* reserved policy element in provider specific buffer */
thread
C++ thread 是std命名空间下支持的多线程。可以在支持C++11的编译器中使用,不论环境。
#include<thread>
using namespace std;
int func1()
{}
int main()
{
//启动线程函数
thread t1(func1);
{
something
other thread
}
//线程同步,保证子线程结束前主线程不结束
t1.join();
}
windows检测按键
#include<conio.h>
int ch;
if (_kbhit())//是否有按键事件
{
ch = _getch();//获取按键键值
cout << "You just type" << ch << endl;
if (ch == 27)//Esc
{
cout << "It's time to see goodbye..." << endl;
break;
}
if (ch == 99)//c
{
cout << "You just want to talk" << endl;
thread talk(chat);
talk.join();
//chat();
}
cout << "Please type your order.\nYou can use Esc for exit or use 'c' for chat.\n" << endl;
}
codeblock 使用socket
需要添加库函数setting->compiler->Link Settings->Link Libraries->add添加libws2_32.a和linwsock32.a路径(C:\Program Files\CodeBlocks\MinGW\lib)
源码
https://download.csdn.net/download/san_junipero/10397080
可改进之处
服务器线程池并发处理请求,防止阻塞