cncml手绘网
标题:
一个简单的C语言的socket服务器和客户端程序
[打印本页]
作者:
admin
时间:
2020-5-9 02:14
标题:
一个简单的C语言的socket服务器和客户端程序
程序很简单,windows环境下的,客户端和服务器程序都在本机运行,在客户端输入发送给服务器的字符串,服务器收到字符串后会打印出来,同时与"abc"比较,与"abc"相同就会返给客户端,输入的是正确的,不同会告诉客户端输入时错误的。
g! r' t) `% ]- j2 ~
客户端程序:client.cpp
. P3 w d! n4 j6 h5 |' ~
#include <winsock2.h>
1 }7 }0 F$ x" Z' s* `3 b& F, j9 l f
#include <stdio.h>
( I' t) F2 ]- M6 i' ~1 Z6 f
#include <stdlib.h>
. z1 u6 K+ D$ B+ P2 v4 H' j4 K
9 ?/ m' I* F) _: f
#define DEFAULT_PORT 5150
7 M# l5 I+ A* d7 K3 @3 K G- o% E/ j
#define DEFAULT_BUFFER 2048
* @6 @; s# H K
& U, `9 S. f1 ~6 y: C: V
char szServer[128],
; W0 F G7 b d# E Z* r
szMessage[1024];
( Q* j; w7 I# t- f
int iPort = DEFAULT_PORT;
. B s+ A7 v9 [- {
( z0 v% h3 ^- c9 n1 g. t5 ?% a& S3 q$ w1 |
int main()
+ x/ J) h6 N. n$ J6 y* A @
{
' f2 t, ]. F! z; W
WSADATA wsd;
3 y* B) C E, T& x
SOCKET sClient;
! v. b3 {/ f$ h: \ U6 g
char szBuffer[DEFAULT_BUFFER];
8 c0 J, U. e2 A# ?
int ret;
8 h k @8 ]3 @# q2 R @
struct sockaddr_in server;
" M) P2 z+ d, G$ b3 D& v8 u! x
struct hostent *host = NULL;
+ n, w( s5 j, P
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
: N4 v+ ^, M! t- ?5 K9 U N
{
$ j) D. O7 y8 H- Y( q
printf("Failed to load Winsock library!\n");
0 b q" `% ?! E( [5 x
return 1;
: X! ?" F8 H8 w. ]) ~+ N
}
9 H( {7 i& Z& ]! m/ f3 l
printf("Enter a string to send to server:\n");
6 U! I6 y; I0 j: ~3 q% Z
gets(szMessage);
6 Q1 [8 i7 _; m/ o: t) I
// Create the socket, and attempt to connect to the server
- l4 a* b* |! c) S: m& K
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/ e$ s1 j/ x& z' a
if (sClient == INVALID_SOCKET)
$ k+ B2 \' J% o( b
{
. W) O, u: P# Z' S/ s2 k* {" F6 q7 }
printf("socket() failed: %d\n", WSAGetLastError());
# z3 K! I% e4 h2 B# Y/ z
return 1;
& c4 O5 i1 a6 q( Z9 b
}
# P+ k/ l# @4 x- {
server.sin_family = AF_INET;
0 ?) o' D' ~% x9 h s
server.sin_port = htons(iPort);
7 u2 ^/ X8 {( i; L4 H# s
server.sin_addr.s_addr = inet_addr("127.0.0.1");
/ G) i& G* \. Q! W4 C$ s( i3 g
* e) i' Z9 b2 N Q5 p( g! H
if (connect(sClient, (struct sockaddr *)&server,
5 h. T2 }% [& q1 R! `
sizeof(server)) == SOCKET_ERROR)
' C/ W% U- E- u& a/ w$ t: `1 ]
{
7 j! J( T1 I# t6 a+ Y
printf("connect() failed: %d\n", WSAGetLastError());
5 U4 X# P/ y5 k; T& w, I( A# ~. `
return 1;
, z0 I! _0 A1 {$ G! j
}
7 B8 ?; n p6 f% b, A
// Send and receive data
' S& _; d2 k6 A
ret = send(sClient, szMessage, strlen(szMessage), 0);
; Y. C: p- f+ j4 y
if (ret == SOCKET_ERROR)
7 f7 \2 w" E9 r# u- W% A
{
' u9 q0 N. q; c& O% `
printf("send() failed: %d\n", WSAGetLastError());
2 t) j7 p; J# j
}
& @9 ^5 N# ~7 ]$ M8 F; v
else{
: u/ d9 y0 m: L2 w8 C" s
printf("Send '%s' \n", szMessage);
. i4 _; W: V+ G' Y( V! [
ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0);
$ R; h7 I6 t/ G( q
if (ret == SOCKET_ERROR){
7 C9 ]! v; ?* a& r2 \0 ^3 W
printf("recv() failed: %d\n", WSAGetLastError());
0 q& j2 i3 Y n
}
! P+ P3 W ]2 h3 W: x
else{
$ O+ ?& Y, g4 m3 l
szBuffer[ret] = '\0';
, @, T( K& Y, G# G
printf("RECV: '%s'\n",szBuffer);
6 W' n5 N( A& H+ U* k
}
* `7 i3 u$ e, V
}
) p( h- v' M) f" t( ?, a7 b
closesocket(sClient);
( h( J* I7 ~2 |7 P R
: m! P9 O4 v) q/ ~$ o
WSACleanup();
- g. s% ^* T7 V/ E: `
return 0;
* t" O6 s7 D o7 j' `
}
复制代码
服务器程序:server.cpp
9 Y5 y! i+ A8 K8 O* L
#include <winsock2.h>
& h; \( q: R' ~3 S* f
#include <stdio.h>
( U9 _9 `& l! T, T( u! I7 X
#include <stdlib.h>
5 k" G0 x9 l& A; }* [
; v4 |& o) W3 n! q, d
#define DEFAULT_PORT 5150
2 B9 A* O' w2 U Z. r4 D8 U
#define DEFAULT_BUFFER 4096
* _& {0 q3 K/ ?3 v1 |
" u+ H4 i3 b3 @2 H. r& ^2 B
int iPort = DEFAULT_PORT;
' E$ g7 x# J" O# b
char szAddress[128];
6 D3 V3 M! B+ n, [! O! d
' {# l+ r9 z6 v
DWORD WINAPI ClientThread(LPVOID lpParam)
7 t! n% B7 u! o/ x! U1 o& K, i
{
, S+ q) F# s0 ? l
SOCKET sock=(SOCKET)lpParam;
) w, ?; m. g! l" p; g
char szBuff[DEFAULT_BUFFER];
) r$ @) T, ?' r. \8 d6 b' H
int ret;
0 S! b2 C+ l4 g- m/ C4 Y3 p( n
6 W7 u& }/ j: k- L
while(1)
; `2 e+ Y2 Y( C# m
{
! P' H/ ^& H) A
ret = recv(sock, szBuff, DEFAULT_BUFFER, 0);
9 P6 D' c6 m! r8 C; l
if (ret == 0)
2 S- d. v6 x* ?6 {
break;
0 O2 S" \, g0 K8 T/ ?
else if (ret == SOCKET_ERROR)
! |' E" U+ O4 M9 }2 W3 s/ z) [
{
! h3 X3 X8 y$ G$ m3 A3 Y
printf("recv() failed: %d\n", WSAGetLastError());
6 b; t9 D7 M3 u6 W
break;
+ |* x0 ?( ^/ J! g
}
% o- C* F6 L: x( k* X% ^$ p: w6 N6 \6 [
szBuff[ret] = '\0';
E8 T' V& x* z3 L% ^
printf("RECV: '%s'\n", szBuff);
9 W% i; s( ]1 x% M8 h4 t( j
if(strcmp(szBuff,"abc")==0){
- e" |( H0 ~! x
memcpy(szBuff,"Send the correct string!",25);
T# s/ d1 g% G5 t% ^9 f1 E. z
}
# c) B5 ?! m* B+ _6 a' L/ Q
else{
$ c: V, k' N% j2 J* d( W6 W
memcpy(szBuff,"Send the wrong string!",23);
9 Z: r2 [ d( [) E# D! ]
}
. I! W% z: Y, m& y8 z
ret = send(sock, szBuff, strlen(szBuff), 0);
% A& [4 d) {, K$ U6 z. v7 ~
if (ret == SOCKET_ERROR){
, [: |; \! {* U; }! [) Q5 k5 `
printf("send() failed: %d\n", WSAGetLastError());
% ], V P, ^+ Q4 U! p& m
}
. n/ Z7 }5 d6 K( U
}
* H; U1 `( ]: t2 O7 r% c |
return 0;
Y. w8 `0 E* ?5 W: w- A, O4 |
}
1 t% C- [9 @$ U# g+ E6 ^- ]
6 u0 ]4 e ^8 T$ o( z+ T
int main()
% P3 R; X) o& H0 t+ B
{
+ s% J2 M" S0 V" d3 M0 |# L2 m
WSADATA wsd;
' C* f# Z6 z& Y/ H, I) s% I1 e. V
SOCKET sListen,
) F3 V e8 D0 A* y8 V9 E: K1 ]
sClient;
[5 J% v( ^1 z7 K+ _+ q) |" G
int iAddrSize;
/ H3 e9 q9 q( K5 Y- a
HANDLE hThread;
; I j+ z: b) x5 k
DWORD dwThreadId;
# R0 |8 S7 R8 p6 K+ T6 Q/ `
struct sockaddr_in local,
$ n/ s' [- }4 W8 \! g, l
client;
P3 \3 M2 b- A' s- r Z1 r# \/ m. ?
. I7 r; d' g* k& T
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
0 R5 y8 M; x- @" k5 T2 h
{
3 V) q6 S$ L% g
printf("Failed to load Winsock!\n");
+ _: r$ b5 Q$ E+ J( g( p; ?9 ~% U
return 1;
$ `* H3 j6 u- N& J: K1 A
}
. h D8 J' f# M
// Create our listening socket
0 N8 S5 W) y$ `; q- Z$ v8 q8 p* a. t
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
. N6 }8 s' n3 _ D" F
if (sListen == SOCKET_ERROR)
: m5 \5 r( G% l2 `
{
+ q) F& L6 `3 E; P/ i0 I+ W% D/ k
printf("socket() failed: %d\n", WSAGetLastError());
C# q9 d3 }1 S) S; ~
return 1;
I1 a0 _' e: K1 C* h
}
0 c9 O/ c4 r( p
local.sin_addr.s_addr = htonl(INADDR_ANY);
5 l- A) ]/ p { K, V0 B1 P
local.sin_family = AF_INET;
. z9 L( [; `+ b
local.sin_port = htons(iPort);
" a) N5 F6 _4 Q
1 T$ g/ Q' E$ @/ D
if (bind(sListen, (struct sockaddr *)&local,
# V- F! T+ u: t5 X! H) h
sizeof(local)) == SOCKET_ERROR)
- T7 F/ U8 v4 {! z
{
5 L4 Y1 H Q E/ V( t- i/ w l
printf("bind() failed: %d\n", WSAGetLastError());
~9 X& i8 p: R+ n _
return 1;
: u3 ~' S; w* V" e) g, g! c. e
}
5 _, B4 G1 A9 u. P- X
listen(sListen, 8);
& C& [) N- ?* V" O6 q. k
// In a continous loop, wait for incoming clients. Once one
# V( u: B1 J6 `. j" M5 G
// is detected, create a thread and pass the handle off to it.
2 [5 l/ u' Y/ T; n
while (1)
/ {' C* g6 Q* I/ a8 j9 u8 U" n W+ B
{
. Q0 w, k1 Z2 }4 v3 T2 b$ t
iAddrSize = sizeof(client);
4 ?- X8 }/ J' u! `: e7 R
sClient = accept(sListen, (struct sockaddr *)&client,
# a1 f# H& `; B1 w7 k. D
&iAddrSize);
. h: z* G6 J6 h* ?+ {
if (sClient == INVALID_SOCKET)
1 F& x" o, u1 {; t) O# t
{
% h; `. g: Z# ^0 R# c- K
printf("accept() failed: %d\n", WSAGetLastError());
5 H( g0 ]# i% s1 |% _
break;
+ F1 c R+ W* {6 b, _6 d
}
( }. g) f0 e' F$ S; K0 C
printf("Accepted client: %s:%d\n",
5 w& f' ~' W; ^. j1 ?& c8 g. X
inet_ntoa(client.sin_addr), ntohs(client.sin_port));
' g8 D% p" K4 z0 s+ R- ~
: b6 @3 H9 |6 }. v
hThread = CreateThread(NULL, 0, ClientThread,
% d/ j. |' M$ c4 j
(LPVOID)sClient, 0, &dwThreadId);
+ x/ y$ L; Q; X! N, n
if (hThread == NULL)
: o1 z% B! o1 m& L8 z( W2 Z, s, I
{
5 ~5 ~* @( M6 c8 Y
printf("CreateThread() failed: %d\n", GetLastError());
$ b' L7 D9 A+ I% O( B2 l6 }
break;
5 n" U' Z( l7 t, ]8 R
}
/ E& z2 t. j/ U" |/ X7 O
CloseHandle(hThread);
# x2 X; H$ a7 b( G/ p
}
) W/ A5 J3 P+ r$ f! P4 T7 V) p
closesocket(sListen);
+ X! L2 C+ h* i) m7 f! H( R
9 L6 n! h* D8 m9 { a& C: M
WSACleanup();
: r a2 L* V& l
return 0;
1 L" C3 k( P7 ?/ O3 p& b2 n
}
复制代码
3 q* C& D& L5 ~7 ]
7 O4 s8 z: X3 h. y9 {* H
5 k+ C: V* I3 c9 b" I/ T. C. X
2 w7 z/ }& L. i& J2 D0 C7 l
6 H- q/ M, X0 L$ B
作者:
admin
时间:
2020-5-9 02:16
作为服务器,你要绑定【bind】到本地的IP地址上进行监听【listen】,但是你的机器上可能有多块网卡,也就有多个IP地址,这时候你要选择绑定在哪个IP上面,如果指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。
2 o5 z. ~' F* i9 W0 ]
" t F3 ~) _0 q, W$ y2 q+ b/ Z
作为客户端,你要连接【connect】到远端的服务器,也是要指定远端服务器的(ip, port)对。
9 y: L" [: q, }" F; g1 D7 C
当然,在这种情况下,不可能将IP地址指定为INADDR_ANY,系统会疯掉的。
8 C: n8 y/ M" X" H+ c% I3 ^
! J8 \3 u" M" e
Server:
" E6 e$ u) l' D4 q
#include <winsock2.h>
$ f, Y6 V! N+ }( S Q
#pragma comment(lib, "WS2_32.lib")
" x8 ^; b8 Y0 J0 z
; R0 M+ { l) n( x) E8 N- c/ r9 b
#include <stdio.h>
z2 T x: d/ ~6 P. x0 b
void main()
& }: Q* R# X# h( a4 j) E. v8 T
{
) \6 B% l9 U) j. S7 N, e
WORD wVersionRequested;//版本号
1 K+ R7 _ t: J( u# M4 n
WSADATA wsaData;
s: `1 z6 x K% X7 n9 h' \
int err;
# _. }/ Y& k4 i; m* d
; R8 f1 \6 m1 E+ @8 V0 N
wVersionRequested = MAKEWORD(2, 2);//2.2版本的套接字
' n) y/ V Q* j* d" p/ y' l
//加载套接字库,如果失败返回
! W' x1 v, J% D! e2 @
err = WSAStartup(wVersionRequested, &wsaData);
6 Q7 m- @& ]: W8 s! P
if (err != 0)
1 ~! m! o2 W' k. B! x
{
9 k' }! A. N6 c1 p* e) H( o' g& T
return;
, e% V; x8 Y* q$ j8 W" c2 z
}
, o6 ?( v$ D+ C' z3 v& X
9 {2 Y9 g0 A4 a$ `& K' D4 i- f
//判断高低字节是不是2,如果不是2.2的版本则退出
+ v( o, R" r' a' _8 D$ q
if (LOBYTE(wsaData.wVersion) != 2 ||
3 y7 t. ]! u" {% C
9 U" @) Q& C! Q4 X+ g' y# m
HIBYTE(wsaData.wVersion) != 2)
" ?" i1 ^# ]* r
# _ Y! \( r- o. a6 E5 L) `, ?
{
$ ~* B- c+ e- W: F
return;
5 `5 Z3 K! ?' k% `
}
5 e8 u: c% \" L+ G
/ w: Z. Z6 K: `$ B
//创建流式套接字,基于TCP(SOCK_STREAM)
* i# n1 e+ ~9 l: |
: L: v. ~5 @5 \5 P
SOCKET socSrv = socket(AF_INET, SOCK_STREAM, 0);
8 l) N. o5 S) o- ~# X: T
2 |, h9 ], M, e- L/ v
//Socket地址结构体的创建
+ w/ B! W, O" e2 E: j
; y5 N/ G3 {+ D) l2 H
SOCKADDR_IN addrSrv;
- Q7 d1 Y) R! t2 ?# x
4 M6 q& q- n$ ?, O, F( [# z' J
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//转换Unsigned long型为网络字节序格
1 |1 v" E$ U7 B/ T* r7 p3 k1 u4 k7 V
addrSrv.sin_family = AF_INET;//指定地址簇
3 @7 X3 n0 h" E. z: c L6 J% ?5 n2 }
addrSrv.sin_port = htons(6000);
# `% ?4 z" x4 ^( X
//指定端口号,除sin_family参数外,其它参数都是网络字节序,因此需要转换
% k8 N( `3 V$ ^2 q3 f
% u+ @. V. u, J" ~1 k
//将套接字绑定到一个端口号和本地地址上
1 Z) Y* E7 p; Q- d- y
bind(socSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//必须用sizeof,strlen不行
( K7 O7 z0 ^& V
' {) b. \6 [2 P; E* [+ P
listen(socSrv, 5);
8 O: C3 U: A; a: a5 m+ A8 Q
, D- j: Y1 a0 g: f% y
SOCKADDR_IN addrClient;//字义用来接收客户端Socket的结构体
& L: F- G. p. ?/ r
1 U& j. v" Y/ ?! m
int len = sizeof(SOCKADDR);//初始化参数,这个参数必须进行初始化,sizeof
- L3 o9 L4 S( N; C* p/ T! Q7 J8 A
$ h# h# h# K) |+ ?3 a0 q% ~9 e
//循环等待接受客户端发送请求
, @6 }4 M, Z/ I: V* [; y. Z
* L) ]1 K l3 E* H& T' m4 j2 t
while (1)
* s% }4 S" Q" M0 }! ?
{
1 `) b; m/ `0 f* {4 q$ `* @
//等待客户请求到来;当请求到来后,接受连接请求,
_* v$ G' j/ ?9 Q1 h3 G
) g( k) Q ]- I; Q( p
//返回一个新的对应于此次连接的套接字(accept)。
$ _% Y/ Y! c* H, v" P3 P# K
//此时程序在此发生阻塞
9 t4 Y& E. S$ ?/ F6 @
" f+ E& W3 z3 e
SOCKET sockConn = accept(socSrv, (SOCKADDR*)&addrClient, &len);
% s3 P0 q" M+ E
2 l) Q: w$ ?4 H6 ?2 K
char sendBuf[100];
& N% k2 r* o6 s/ r
3 o; t8 e' e6 E3 f& u k
sprintf(sendBuf, "Welcome %s to JoyChou",
, r, y2 E9 _% ?# ^; g0 |
2 N8 a I2 T' X4 @: p
inet_ntoa(addrClient.sin_addr));//格式化输出
+ c3 G+ z% Z% Z! z: c2 E& H5 B
2 U% O8 ~% _- H; Y- G- }# f' m
//用返回的套接字和客户端进行通信
8 r, g% j, l7 P I9 s. w- L
6 @( n3 c, x, W5 M2 q4 z
send(sockConn, sendBuf, strlen(sendBuf)+1, 0);//多发送一个字节
, }3 n3 s7 @. A8 W6 n+ Y
( [2 {3 ?+ r" g2 n
//接收数据
% I& O" }6 N* z; `# T0 L
2 L: N+ q9 K* I1 b1 X: E1 S* [
char recvBuf[100];
5 Y! m" I% D. I' U3 i5 v
# r, \: q+ M5 |: M! P" s
recv(sockConn, recvBuf, 100, 0);
, [4 O8 g/ n, {: o" ~2 E+ o$ |
6 g# B$ X) A* ` M
printf("%s\n", recvBuf);
% b" R" X. w i& V# Q5 J
closesocket(sockConn);
. L4 ~ }0 j6 b5 ?
/ W7 b- |# S, A! W. f3 @9 G
}
; w) e) P+ V2 }6 t7 Z
}
9 i8 ^' f& \" n% z, K% v
$ y& q" Z/ B/ y# t
, J& x& [" Q* {1 j
Client:
% X: c* d7 z7 d5 K4 }% U2 x& y
+ P4 S1 `) l/ j( I
#include <winsock2.H>
2 ^7 l' U9 d v
#pragma comment(lib, "Ws2_32.lib")
+ e) Y8 C/ p5 B( g8 H+ {
#include <stdio.h>
6 _/ V! s6 ~# k/ Q' o8 [4 a. c
#include <string.h>
& U( H, V' \) P6 A) D4 |4 \1 \
& u! H. ^6 ?2 W7 u: L. g
void main()
3 K7 X" A# Z8 Z0 d! Q
{
( S. Q1 y% t, \( y4 F# r. l
WORD wVersionRequested;
$ G& M, }& E$ I
WSADATA wsaData;
. S# k3 |. |5 w" H
int err;
2 Z8 x# M3 Y& U+ A+ s' _# M8 C
1 d6 T" ^$ ?1 d& R0 Q
wVersionRequested = MAKEWORD( 2, 2 );
; w }$ A7 V# j# d0 F; [
3 }! h2 f" W& k. M
err = WSAStartup( wVersionRequested, &wsaData );
( I. U8 j4 Y) S7 i
if ( err != 0 ) {
7 [- N9 J# l! @" X
7 o- m, Z& p- ^; b( |: ]
return;
! `. u7 ^ \ [
}
2 ~* R8 F3 h. ?4 R, x: h3 Y
% D# U0 t [7 _
$ F6 w8 y* [6 s' |' A( d+ b
if ( LOBYTE( wsaData.wVersion ) != 2 ||
9 M" i' J; k- D. j- ~* X
HIBYTE( wsaData.wVersion ) != 2 )
2 L+ p" j4 s% w) `7 O
{
) I/ [, l+ }4 Y6 N9 f0 F# o p9 Z. T
( E( N, T) d) o4 J2 B: J/ P
WSACleanup( );
- K" @0 B6 P; _" X
return;
, N5 i7 w" N1 m! r/ ?; N9 m: L
}
: A( P! A+ ^- N. f( ?" Y
SOCKET socketClient = socket(AF_INET, SOCK_STREAM, 0);
4 C' Q1 w* I+ |8 Q+ M: r
SOCKADDR_IN addrSrv;
^3 A0 S2 f+ A5 v
addrSrv.sin_family = AF_INET;
- h- H; l1 V& G, D5 q
addrSrv.sin_port = htons(6000);
* H# Y4 K" c9 c+ T8 O% J2 m m
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.101");//服务器端的IP地址
- @" J& g E* y: b4 P E/ D
4 g9 J4 K% r, o/ H |
connect(socketClient, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
2 J" W3 `( z2 F2 r
char recvBuf[256];
- \6 \% F2 }( Z7 \6 }- U6 x0 H. ^
char sendBuf[] = "This is Joychou";
& b( V7 [" \' [- _( K1 L
recv(socketClient, recvBuf, 256, 0);
- o9 Y3 s5 S3 T3 K9 P3 ~
printf("%s\n", recvBuf);
+ G0 b( @6 Z. w' X) ?+ T
send(socketClient, sendBuf, strlen(sendBuf) + 1, 0);
8 q2 q: z1 K: }2 i
closesocket(socketClient);
7 z/ D5 e3 E$ F7 S3 [7 V7 u
WSACleanup();
+ G( q e5 u$ i8 g/ L5 T
& s+ [( k& I, Z; {
}
复制代码
* @- r! r, K& W. y
3 p: h& L& ]: F
欢迎光临 cncml手绘网 (http://bbs.cncml.com/)
Powered by Discuz! X3.2