关于socket的文章,园子里面有很多,其实无非就是 WSAStartup、socket、bind、listen、accept、recv、send(服务端),WSAStartup、socket、connect、send、recv(客户端)的使用。今天第一次看socket,也只学会了socket阻塞模式,即一个服务端对一个客户端,别的客户端想连接也连接不上--个人理解,不知道对不对。
为了解决一(服务端)对多(客户端)的问题,自作聪明在服务端用上了多线程。初步效果还不错:
我晕,1080P的笔记本上截的图,放上来这么大!
上代码,
TCPServer.cpp
1 /**************************************************************************** 2 /* 文 件 名:server.cpp * 3 /* 作 者:ZhangXuliang * 4 /* 日 期:2015/05/24 星期日 11:28 * 5 /* 版 本 号:V 1.0.0 * 6 /* 版权说明:Copyright(c)2015 ZXL. All rights reserved. * 7 /* 描 述:多线程处理Client连接 * 8 /****************************************************************************/ 9 10 #define _CRT_SECURE_NO_WARNINGS 11 12 #include <iostream> 13 #include <windows.h> 14 #include <process.h> 15 16 //#include <WinSock2.h> 17 #pragma comment(lib,"Ws2_32.lib") 18 19 #define SERVER_IP "127.0.0.1" 20 #define SERVER_PORT 9999 21 #define BUFSIZE (1024) 22 23 //定义一个结构体,存储客户端的sClient、sockaddr_in结构 24 typedef struct _ClientStruct 25 { 26 SOCKET csSocket; 27 SOCKADDR_IN csSockAddr_In; 28 } ClientStruct, *LPClientStruct; 29 30 //处理后续客户端事件的程序 31 void ClientEven(PVOID param); 32 33 using namespace std; 34 int main() 35 { 36 int ErrCode; //错误代码 37 int addrlen; 38 WSADATA WSAdata; 39 SOCKET sServer, sClient; 40 SOCKADDR_IN saServer, saClient; 41 42 //初始化 43 ErrCode = WSAStartup(MAKEWORD(2, 2), &WSAdata); 44 if (ErrCode) 45 { 46 cout << "WSAStartup()出错!错误代码:#" << ErrCode << endl; 47 WSACleanup(); 48 return -1; 49 } 50 51 //创建套接字 52 sServer = socket(AF_INET, SOCK_STREAM,0); 53 if (INVALID_SOCKET == sServer) 54 { 55 cout << "socket()出错!错误代码:#" << WSAGetLastError() << endl; 56 WSACleanup(); 57 return -2; 58 } 59 60 //初始化saServer 61 saServer.sin_family = AF_INET; 62 saServer.sin_addr.S_un.S_addr = htons(INADDR_ANY); 63 saServer.sin_port = htons(SERVER_PORT); 64 //绑定监听IP和端口,以便下面的listen()监听 65 ErrCode = bind(sServer, (SOCKADDR *)&saServer, sizeof(SOCKADDR_IN)); 66 if (SOCKET_ERROR == ErrCode) 67 { 68 cout << "bind()出错!错误代码:#" << WSAGetLastError() << endl; 69 WSACleanup(); 70 return -3; 71 } 72 73 //开始监听 74 ErrCode = listen(sServer, 10); 75 if (SOCKET_ERROR == ErrCode) 76 { 77 cout << "listen()出错!错误代码:#" << WSAGetLastError() << endl; 78 WSACleanup(); 79 return -4; 80 } 81 cout << "正在监听端口:" << SERVER_PORT << endl; 82 83 while (TRUE) 84 { 85 //接受来自客户端的连接请求 86 addrlen = sizeof(saClient); 87 sClient = accept(sServer, (SOCKADDR *)&saClient, &addrlen); 88 if (INVALID_SOCKET == sClient) 89 { 90 cout << "accpet()出错!错误代码:#" << WSAGetLastError() << endl; 91 } 92 93 //启动一个线程,来处理后续的客户端事件 94 LPClientStruct lpcsClient = new ClientStruct; //个人觉得这儿用 new 是一个创举啊,因为在启动线程后,如果直接将ClientStruct指针 95 lpcsClient->csSocket = sClient; //传到ClientEven()中,那么,当有中一个Client接入时,会改变ClientEven中的ClientStruct内容 96 lpcsClient->csSockAddr_In = saClient; //所以这儿用new,再在线程中delete 97 _beginthread(ClientEven,0, lpcsClient); 98 99 } 100 WSACleanup(); 101 return 0; 102 } 103 104 105 void ClientEven(PVOID param) 106 { 107 int ErrCode; 108 109 char buf[BUFSIZE] = { 0 }; 110 SYSTEMTIME time; 111 GetLocalTime(&time); 112 sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); 113 114 //设置下输出的字体的颜色 115 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 116 SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN); 117 cout << buf << "\t" << inet_ntoa(((LPClientStruct)param)->csSockAddr_In.sin_addr) << ":" 118 << ((LPClientStruct)param)->csSockAddr_In.sin_port << "\t接入服务器" << endl; 119 //还原字体颜色 120 SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY); 121 //连接成功,先发送一条成功的消息 122 ErrCode = send(((LPClientStruct)param)->csSocket, "连接服务器成功", strlen("连接到服务器成功!"),0); 123 if (SOCKET_ERROR == ErrCode) 124 { 125 cout << "send()出错!错误代码:#" << WSAGetLastError() << endl; 126 delete (LPClientStruct)param; 127 _endthread(); 128 } 129 130 while (TRUE) 131 { 132 //接受消息 133 ZeroMemory(buf, BUFSIZE); 134 ErrCode = recv(((LPClientStruct)param)->csSocket, buf, sizeof(buf), 0); 135 if (SOCKET_ERROR == ErrCode) 136 { 137 if (WSAGetLastError() == 10054) 138 { 139 SetConsoleTextAttribute(hConsole, FOREGROUND_RED); 140 cout << inet_ntoa(((LPClientStruct)param)->csSockAddr_In.sin_addr) << ":" << ((LPClientStruct)param)->csSockAddr_In.sin_port << "\t被强制断开连接!" << endl; 141 SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY); 142 } 143 else 144 { 145 cout << "recv()出错!错误代码:#" << WSAGetLastError() << endl; 146 } 147 148 delete (LPClientStruct)param; //退出线程之前先delete前面new的ClientStruct 149 _endthread(); 150 } 151 152 cout << inet_ntoa(((LPClientStruct)param)->csSockAddr_In.sin_addr) << ":" << ((LPClientStruct)param)->csSockAddr_In.sin_port << "\t说:" << buf << endl; 153 154 //反馈消息 155 //if ((pPipe = _popen(buf, "rt")) != NULL) 156 //{ 157 // while (!feof(pPipe)) 158 // { 159 // fgets(szResult, 32, pPipe); 160 // strcat(buf, szResult); 161 // if (strlen(buf) == 1024) 162 // { 163 // buf[1024] = 'c'; //c --continue 164 // send(((LPClientStruct)param)->csSocket, buf, sizeof(buf), 0); 165 // ZeroMemory(buf, BUFSIZE); 166 // } 167 // } 168 // _pclose(pPipe); 169 //} 170 //else 171 // strcpy(buf, "打开匿名管道失败!"); 172 173 ErrCode = send(((LPClientStruct)param)->csSocket, buf, sizeof(buf), 0); 174 175 if (SOCKET_ERROR == ErrCode) 176 { 177 cout << "send()出错!错误代码:#" << WSAGetLastError() << endl; 178 179 delete (LPClientStruct)param; //退出线程之前先delete前面new的ClientStruct 180 _endthread(); 181 } 182 } 183 delete (LPClientStruct)param; //退出线程之前先delete前面new的ClientStruct 184 _endthread(); 185 }
下面是TCPClient.cpp
1 /**************************************************************************** 2 /* 文 件 名:Client.cpp * 3 /* 作 者:ZhangXuliang * 4 /* 日 期:2015/05/23 星期六 23:30 * 5 /* 版 本 号:V 1.0.0 * 6 /* 版权说明:Copyright(c)2015 ZXL. All rights reserved. * 7 /* 描 述:Client客户端 * 8 /****************************************************************************/ 9 10 #define _CRT_SECURE_NO_WARNINGS 11 12 #include <iostream> 13 #include <string> 14 15 #include <winsock.h> 16 #pragma comment(lib,"ws2_32.lib") 17 18 using namespace std; 19 20 #define BUFSIZE 1024 21 #define PORT 9999 22 //#define SERVER_IP "127.0.0.1" 23 24 int main() 25 { 26 WSAData wsaData; 27 SOCKET sHost; 28 sockaddr_in addrServ; 29 char buf[BUFSIZE]; 30 int retVal; 31 32 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) 33 { 34 cout << "WSAStartup()失败!" << endl; 35 return -1; 36 } 37 38 sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 39 if (INVALID_SOCKET == sHost) 40 { 41 cout << "socket()出错!" << endl; 42 WSACleanup(); 43 return -1; 44 } 45 46 addrServ.sin_family = AF_INET; 47 addrServ.sin_port = htons(PORT); 48 addrServ.sin_addr.S_un.S_addr = inet_addr("192.168.1.223"); 49 50 retVal = connect(sHost, (LPSOCKADDR)&addrServ, sizeof(addrServ)); 51 if (SOCKET_ERROR == retVal) 52 { 53 cout << "connect()出错!" << endl; 54 closesocket(sHost); 55 WSACleanup(); 56 return -1; 57 } 58 retVal = recv(sHost, buf, sizeof(buf) + 1, 0); 59 cout << "从服务器接受:" << buf << endl << endl << endl; 60 while (TRUE) 61 { 62 cout << "输入要发给服务器的内容:"; 63 //string msg; 64 //getline(cin.msg); 65 char msg[BUFSIZE]; 66 //cin.getline(msg, BUFSIZE); 67 cin >> msg; 68 //cout << endl; 69 ZeroMemory(buf, BUFSIZE); 70 strcpy(buf, msg); 71 72 retVal = send(sHost, buf, strlen(buf), 0); 73 if (SOCKET_ERROR == retVal) 74 { 75 cout << "发送失败!" << endl; 76 closesocket(sHost); 77 WSACleanup(); 78 return -1; 79 } 80 81 retVal = recv(sHost, buf, sizeof(buf) + 1, 0); 82 if (SOCKET_ERROR == retVal) 83 { 84 cout << "recv()出错!错误代码:#" << WSAGetLastError() << endl; 85 closesocket(sHost); 86 WSACleanup(); 87 return -1; 88 } 89 cout << "从服务器接受:" << buf << endl << endl; 90 91 92 if (strcmp(buf, "quit") == 0) 93 { 94 cout << "quit" << endl; 95 break; 96 } 97 } 98 99 closesocket(sHost); 100 WSACleanup(); 101 102 return 0; 103 104 105 106 }