Winsocket入门教程一多线程阻塞式服务器和阻塞式客户端程序TCPWord文档格式.docx
- 文档编号:20006105
- 上传时间:2023-01-14
- 格式:DOCX
- 页数:19
- 大小:56.10KB
Winsocket入门教程一多线程阻塞式服务器和阻塞式客户端程序TCPWord文档格式.docx
《Winsocket入门教程一多线程阻塞式服务器和阻塞式客户端程序TCPWord文档格式.docx》由会员分享,可在线阅读,更多相关《Winsocket入门教程一多线程阻塞式服务器和阻塞式客户端程序TCPWord文档格式.docx(19页珍藏版)》请在冰豆网上搜索。
8.#include
cassert>
9.#include
WinSock2.h>
10.#include
process.h>
11.#pragma
comment(lib,
"
ws2_32.lib"
)
12.#define
ASSERT
assert
13.#define
THREAD
HANDLE
14.#define
EVENT
15.#define
CloseThread
CloseHandle
16.#define
CloseEvent
17.using
std:
:
cin;
18.using
cout;
19.using
endl;
20.////////////////////////////////////////////////////////////////////////////////////////////////////
21.///
\struct
tagServerRecv
22.///
23.///
线程函数参数结构体,其中包含已建立连接的socket.
24.///
25.///
\author
Shining100
26.///
\date
2010-05-18
27.////////////////////////////////////////////////////////////////////////////////////////////////////
28.typedef
struct
29.{
30.
SOCKET
skAccept;
//
已建立连接的socket
31.
CRITICAL_SECTION
*pcs;
同步控制台输出的临界区
32.
e;
保证结构体各个字段在结构体字段改变之前将其拷贝到线程中的信号量
33.
t;
当前线程的内核对象
34.
DWORD
dwThreadID;
当前线程的ID
35.}SERVER_RECV,
*PSERVER_RECV;
36.////////////////////////////////////////////////////////////////////////////////////////////////////
37.///
\fn
static
int
ServerRecv(LPVOID
lParam)
38.///
39.///
服务器与建立连接的客户端进行通讯.
40.///
41.///
42.///
43.///
44.///
\param
lParam
线程函数参数,
详细信息见上面说明.
45.///
46.///
\return
总是返回0.
47.////////////////////////////////////////////////////////////////////////////////////////////////////
48.static
lParam);
49.static
const
c_iPort
=
10001;
50.int
main()
51.{
52.
iRet
SOCKET_ERROR;
53.
初始化Winsocket,所有Winsocket程序必须先使用WSAStartup进行初始化
54.
WSADATA
data;
55.
ZeroMemory(&
data,
sizeof(WSADATA));
56.
WSAStartup(MAKEWORD(2,
0),
&
data);
57.
ASSERT(SOCKET_ERROR
!
iRet);
58.
建立服务端程序的监听套接字
59.
skListen
INVALID_SOCKET;
60.
socket(AF_INET,
SOCK_STREAM,
0);
61.
ASSERT(INVALID_SOCKET
skListen);
62.
初始化监听套接字地址信息
63.
sockaddr_in
adrServ;
表示网络地址
64.
adrServ,
sizeof(sockaddr_in));
65.
adrServ.sin_family
AF_INET;
初始化地址格式,只能为AF_INET
66.
adrServ.sin_port
htons(c_iPort);
初始化端口,由于网络字节顺序和主机字节顺序相反,所以必须使用htons将主机字节顺序转换成网络字节顺序
67.
adrServ.sin_addr.s_addr
INADDR_ANY;
初始化IP,由于是服务器程序,所以可以将INADDR_ANY赋给该字段,表示任意的IP
68.
绑定监听套接字到本地
69.
bind(skListen,
(sockaddr*)&
70.
71.
使用监听套接字进行监听
72.
listen(skListen,
SOMAXCONN);
SOMAXCONN表示可以连接到该程序的最大连接数
73.
74.
输出控制台缓冲区,由于可能有多个客户端程序可能同时向缓冲区发送请求信息
75.
为了保证输出时能够一次性完整的输出完一个客户端的请求信息,所以在输出客
76.
户程序的信息到控制台时,必须使用临界区阻塞其它线程
77.
cs;
78.
InitializeCriticalSection(&
cs);
79.
80.
因为当该结构体拷贝到线程中之前,
有可能有新的连接到来并改变了结构体的值
81.
所以我们必须先保证值拷贝过后再接受连接
82.
e
NULL;
83.
CreateEvent(NULL,
FALSE,
NULL);
84.
ASSERT(NULL
e);
85.
for(;
;
86.
{
87.
客户端向服务器端发送连接请求,服务器端接受客户端的连接
88.
skAccept
89.
adrClit;
90.
adrClit,
91.
iLen
sizeof(sockaddr_in);
92.
accept(skListen,
iLen);
如果没有客户端程序请求连接,服务端程序会一直阻塞在这里等待连接
93.
skAccept);
94.
SERVER_RECV
sr;
95.
成功创建连接后创建一个独立的线程应答客户请求,以防止应用程序因为阻塞无法应答新的客户请求
96.
我们应该先将线程挂起,以便我们能够在线程执行之前初始化线程所需要的结构体变量中的各个字段
97.
hThread
98.
dwThreadID
0;
99.
CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ServerRecv,
100.
sr,
CREATE_SUSPENDED,
dwThreadID);
101.
hThread);
102.
初始化结构体字段
103.
sr.skAccept
104.
sr.pcs
105.
sr.e
106.
sr.t
hThread;
107.
sr.dwThreadID
108.
启动线程
109.
dwRet
ResumeThread(hThread);
110.
ASSERT(-1
dwRet);
111.
112.
保证结构体被拷贝到线程中后再应答新的连接
113.
WaitForSingleObject
(e,
INFINITE);
114.
ASSERT(WAIT_FAILED
115.
}
116.
清理线程同步资源
117.
DeleteCriticalSection(&
118.
BOOL
bRet
FALSE;
119.
CloseEvent(e);
120.
ASSERT(bRet);
121.
关闭该套接字的连接
122.
shutdown(skListen,
SD_SEND);
123.
124.
清理该套接字的资源
125.
closesocket(skListen);
126.
127.
清理Winsocket资源
128.
WSACleanup();
129.
130.
cin.get();
131.
return
132.}
133.int
134.{
135.
拷贝结构体各个字段到线程中
136.
PSERVER_RECV
psr
(PSERVER_RECV)lParam;
137.
sr
138.
psr->
skAccept,
139.
pcs,
140.
e,
141.
t,
142.
dwThreadID
143.
};
144.
设置信号量,
使主线程能够接受新的连接
145.
146.
SetEvent(sr.e);
147.
148.
c_iBufLen
512;
149.
char
szBuf[c_iBufLen
+
1]
{'
\0'
150.
c_szPrefix[]
Server
recv:
151.
c_iPrefLen
strlen(c_szPrefix);
152.
szRely[c_iBufLen
16
153.
strcpy(szRely,
c_szPrefix);
154.
155.
156.
157.
recv(sr.skAccept,
szBuf,
c_iBufLen,
接收客户端发送的信息,
如果客户端不发送信息,则线程会阻塞到此处
158.
if(0
==
iRet)
客户端优雅的关闭了此连接
159.
160.
cout
Connection
shutdown."
161.
break;
162.
163.
else
if(SOCKET_ERROR
客户端粗鲁的关闭了此连接或者接受信息出错
164.
165.
recv
error."
166.
167.
168.
szBuf[iRet]
'
169.
EnterCriticalSection(sr.pcs);
170.
says:
szBuf
输出接收到的信息
171.
LeaveCriticalSection(sr.pcs);
172.
向客户端发送信息
173.
strcpy(szRely
c_iPrefLen,
szBuf);
174.
send(sr.skAccept,
szRely,
strlen(szRely),
客户端如果没有足够的缓冲区接受信息,则线程会阻塞到此处
175.
iRet)
176.
177.
send
178.
179.
180.
181.
关闭该套接口
182.
shutdown(sr.skAccept,
183.
while(recv(sr.skAccept,
0)
>
184.
185.
清理该套接口的资源
186.
closesocket(sr.skAccept);
187.
188.
关闭该线程对象
189.
CloseThread(sr.t);
190.
191.
exit."
192.
193.}
在用Winsocket编写程序时,我们首先必须要进行如下的操作,以为该进程初始化Winsocket和Ws2_32.dll,而使后面的函数调用有效。
1.WSADATA
2.ZeroMemory(&
3.iRet
4.ASSERT(SOCKET_ERROR
WSAStartup第一个参数为要使用的Winsocket的版本,MAKEWORD(2,0)表示我们使用Winsocket2.0。
第二个参数在
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Winsocket 入门教程 多线程 阻塞 服务器 客户端 程序 TCP