關於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 }