管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。/ m3 l: m8 e1 x, Z* G: L4 i: e4 h
6 q* I3 A/ m5 h: D' n9 B: i6 U
* j; v# m8 d C$ _; ?socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 S" C' K, i1 b6 \6 Z, y
$ }- d4 A4 x2 K) m6 k7 \" c4 @% D5 s8 h
* s1 |* J9 b1 ?% rTCP协议3 Q2 Z) l' U7 W/ O) _
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。4 ? j# i8 z; y6 M$ |: t$ C% Z
5 Y# K$ {- ^" x: p" M. i2 p' x1 r; ^, |" s, l2 s: w+ ]$ B
关键词:三次握手,可靠,基于字节流。' V+ Z/ @, w6 J9 V* N P0 E& C
8 I9 ~+ A+ j8 x' T
* ?. _' Z2 j! L6 I: I4 Z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 a, H$ h& U2 o8 k* O
: u, y9 a) k( vTCP服务器端和客户端的运行流程
8 S% ^ h2 B0 U9 |: ^/ Y( u0 w5 ~1 e5 B$ [7 h: E2 k# C
0 v/ a' R& w% @1 N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
3 F! @6 l/ E4 i1 N' Y$ k9 k' E2 e3 ~9 | J8 s5 g/ v
! h, o; j/ S4 j( ^/ A: m9 L! x1.创建socket
, g1 x" O' v) k! n socket是一个结构体,被创建在内核中
2 f& p9 X* n8 r4 l0 _ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ k2 }' z% |1 D8 }3 }# U! k& A9 D
- x' t# z8 H) e, s& d) V/ o0 c
# w; u( E. [+ s3 J" u2.调用bind函数$ ^! e) L2 T0 m# A; _' Z. T0 B3 ?
将socket和地址(包括ip、port)绑定。
" b6 V6 N, o! }9 W 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序6 y+ w. p6 s! X
struct sockaddr_in myaddr; //地址结构体. y- W+ ~. N5 a1 I6 G1 w
bind函数, r& N& D, x* O" a
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))( V% X; M0 v* M$ H- _
( R) X, c" O; E4 q% i
* A1 T: N; E# E' s0 D9 q3.listen监听,将接收到的客户端连接放入队列
' d# t4 }" o/ E: a; R2 q7 F- Z listen(sockfd,8) //第二个参数是队列长度) a4 f) c6 F8 F
% }! Z5 Q# ?) w/ H, h1 M. e0 D4 r3 t7 Z" ^: P: O& V' u4 s
4.调用accept函数,从队列获取请求,返回socket描 述符
Q6 f5 Y/ e+ ~" w1 X 如果无请求,将会阻塞,直到获得连接
3 \+ a: A+ z. b' a! K4 S5 h: C) j, g int fd=accept(sockfd, NULL,NULL);//这边采用默认参数8 ^- m6 X J( a* P8 ?$ k0 k
8 |. M7 C7 B3 q5 F
! Q' S, _7 i9 K; `) ?" Y( V5.调用read/write进行双向通信
$ H) C& w: E3 b$ v5 i# s, A
) `7 r3 T e( N
% i% M" w$ X% K& f, T2 K. w4 o6.关闭accept返回的socket
, G* q9 B# u! r! i close(scokfd);/ c2 m9 I2 i( Z2 b- \
s, {4 n$ {+ o; t7 e3 q9 C6 W
m0 ]* C" u- |: F
5 i3 P! o1 J$ m7 @' c4 g* _/ @9 A4 K$ a. L- K( ~5 B: G
下面放出完整代码
, U" z; S' p, A, y, g- ]( S# j* i+ c( E
- /*服务器*/
% M; d) P4 \- ?! E2 t& D% S* ~ - #include <stdio.h>- T6 u: ^( m0 U0 q
- #include <string.h>
. ~) M6 \ i9 T$ s% @; I - #include <stdlib.h>
0 ~ S7 f6 j5 m4 [# Q0 f - #include <strings.h>
5 \" F& g. m8 Y* w - #include <sys/types.h>
3 y( @% Y$ h2 n( T2 v Q - #include <sys/socket.h>
$ h8 A6 @) W( {0 ]: t! m - #include <arpa/inet.h>3 |) f) t& d) `4 B; [
- #include <netinet/in.h># V9 k1 r- A5 P( U
- int main()
) b5 C. N" Y4 w1 v$ }7 W; X* t - {
, P0 F& B% A' l0 w$ m$ w( ^ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字5 N: F9 k" b6 s2 Q
- if (sockfd < 0)
! p3 F2 C5 z ` A/ v6 b! q4 k - {
* @3 P7 G1 Q- o" R- @3 c - perror("socket");
* g2 D, f J. O* y& w M - return -1;
4 C5 s8 m; A6 T# L% s - } //创建失败的错误处理
v* A5 S2 I: P' |6 C1 L2 b - printf("socket..............& U( K3 W9 X- {" N
- "); //成功则打印“socket。。。。”) r8 ^. E x6 j, F
-
+ C# f2 b0 y/ y5 ^+ V0 W6 z! v - struct sockaddr_in myaddr; //创建“我的地址”结构体
3 U ]1 z) ]* `# I/ i - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
+ m! L {5 ]2 ~+ x" m0 \: k - myaddr.sin_family = AF_INET; //选择IPV4地址类型
- a$ C! ]2 B4 Y6 n: @ - myaddr.sin_port = htons(8888); //选择端口号+ w% u5 \: b5 L' V+ G X3 e% [
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
/ ], B* c/ f5 y -
* G1 ~, L/ t" I6 A# e' u8 G$ V! x - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 ^- N& x9 |1 g7 z/ u Q
- {
$ ]8 ]8 O( j- _- R; A/ g+ X9 U - perror("bind");
+ k9 d* T7 f! C& }& t8 @" o3 o - return -1;
1 P; M9 o( h: `! L - }
5 X3 x& F$ {6 C - printf("bind..........
7 V3 n" _; M+ T3 {8 i, j; { - ");7 d' q! Q- m% J$ I" R0 G4 u3 c
- 3 Q% [+ X2 y' {; v# t
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- q0 N2 H, J( n+ P" m: i0 E/ a
- {1 D" a$ M8 M4 v9 u, V. n
- perror("listen");
A6 @" t ~2 c - return -1;" }) ^; u; f& y+ f9 D
- }7 J- G7 L1 O5 A0 u: {
- printf("listen............" ]( Z7 y/ k4 D2 H
- ");
p( Z/ e/ `; x0 C" M# J6 D -
' ]% z. p: y2 |$ B# J+ Z, ?0 v - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求- r7 `/ H- {( a/ G) K
- if (connfd < 0)) L5 Z# o' ]; ~" p- G3 E
- {
$ P) _! [# Y7 A7 W" |2 z. B - perror("accept");9 r: Y( P/ S1 u3 i
- return -1;
% j7 M1 ^" K9 t, ? - }
; Z0 c x& l1 s0 U! E+ r8 K - printf("accept..............5 N. W! J+ E: F; K! }/ {
- ");& D+ t: N) a/ A2 z
- char buf[100];//定义一个数组用来存储接收到的数据
5 ~& J3 E2 z# S - int ret; |' I* C* c) }6 W
- while (1)
( R# d5 _- g) Z$ N# n - {
( R+ W# X3 ^5 Y) s; G - memset(buf, 0, sizeof(buf));
) C( B" K- x$ L, B4 _& J! \ - ret = read(connfd, buf, sizeof(buf));
8 e- I: ^) H' l - if (0 > ret)# ?( \% G: |2 p- c9 a
- {" E) ^" s/ o9 v: W- U. u' ^. x
- perror("read");1 P9 c% q% P) s
- break;
" v: ^3 K0 _$ u* S - }//执行while循环读取数据,当7 R2 }2 t3 [2 A E+ }4 F1 x/ r
- else if (0 == ret)1 q, s! M+ i" K& a6 H) H
- {
! D0 u6 }; c9 M5 u# i - printf("write close!
% P% i, T- A, F1 J - ");" X, R# s0 v5 G& s- _3 Y
- break;
$ a! I8 i1 b2 v. F) [ - }$ }- J$ R( L( }2 D. v
- printf("recv: "); G S3 N; z/ Z1 p: ^0 H. z" V
- fputs(buf, stdout);//打印接收到的数据
7 c0 e0 M: V' {/ W3 l d: ?3 ^$ ^ - }: k4 C% @5 S- S6 m! y3 n6 g
- close(sockfd);//关闭套接字
" B! y- a: _9 V t1 ? - close(connfd);//断开连接
! t! | z' [. Z* u/ Y; ~ f - return 0;* Y, N9 q' j% d4 M+ `4 Z) i
- }
复制代码
- z2 u9 q( ~% f% K- L5 f* t" q. f+ f) }! _+ N' S W6 h3 G
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
% @! ^: @3 W: k1 }; v% V* u" D$ j - #include <stdio.h>5 a- Q- z( s# U" a
- #include <string.h>$ @2 N" |6 c) g5 Q# g- v
- #include <stdlib.h>$ R- O k6 T. f
- #include <strings.h>
" X' d2 \) B% i8 D1 z - #include <sys/types.h>
+ g2 H1 a$ I% L9 K) B8 B - #include <sys/socket.h>$ m9 b2 y, A4 X* h+ }3 f
- #include <netinet/in.h>) f2 o" N/ y- q5 f
- #include <arpa/inet.h>! ^7 u& N/ b6 g8 c2 E' o. v; d1 s
- int main()
. B4 P( A, K. L9 i3 r: n - {
. Q* t" @1 i* _* P6 V8 s - int sockfd;
* Q: g7 T$ X& `. u, G - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))), B# ^9 }. d Q6 p% V @3 ]
- {) ^1 \! G+ r) n" A; U
- perror("socket");/ O# b# t( w4 K
- return -1;, I2 ~. B& j: [) z
- }/ n+ ]! }. m2 g2 F
- printf("socket...........( n, ~9 H' V' K. e9 g& w* g) @
- ");
" B+ f3 O4 j* y/ F8 } - \7 D, \* F5 ~5 s& U% q u9 ^
- struct sockaddr_in srv_addr;# `8 N# b$ I% \7 t9 e1 l- {
- memset(&srv_addr, 0, sizeof(srv_addr));
% Q. c% {3 R _* S6 a+ z - srv_addr.sin_family = AF_INET;
9 u/ J* c# W* i2 M' _' s - srv_addr.sin_port = htons(8888);
5 b. Z) o+ f s2 o - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");5 d, p; ?# d: x7 M1 s0 q
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))+ F2 g$ B% H4 T5 z5 N/ W; ]
- {
1 ]! `% H5 F5 G8 C; ^% s" @! D6 I - perror("connect");
' c v9 X9 x+ B - return -1; //exit //pthread_exit* y, n: M/ Q- ]4 a2 t8 p& P
- }7 {$ M5 ]" O, a/ O
- printf("connect..............
# c! V* m) d$ w9 p: l - ");
. ~+ J+ J& E0 U1 T - char buf[100];
$ e; O8 P- p8 _% w& E* u7 A- x - int ret;4 f( m5 A- r3 F
- while (1)
0 J7 B& d: ?3 _2 Y7 e: d - {
8 H/ ?" f k" o& c9 q0 X( |4 k - printf("send: ");: {5 R- o. \, N
- fgets(buf, sizeof(buf), stdin);; m+ i. C/ m& \) l
- ret = write(sockfd, buf, sizeof(buf));
0 T6 S8 H) |8 Y8 p' Z8 Y - if (ret < 0)
2 S( y6 h' d" W0 F G5 \# E% I# @ - {
2 g% ?5 C2 L# C - perror("write");
8 G& z3 h# u& g7 |# E R; R - break;! i; K1 w) W% B" D
- }
$ e( J: g$ M! o* ] - if (strncmp(buf, "quit", 4) == 0)
7 |- S- _! B( y - break;2 } z/ @- A" ^* u2 H2 ]5 d& d
- }
U& y% O0 w1 o, b3 Y8 \& F5 y - close(sockfd);
9 d, W; M k' z- M# i - return 0;& c8 k9 t( ^5 M; w
- }
复制代码 $ ^# i9 C T9 M f9 v2 M
- q$ |8 I4 q5 u* ]$ d/ M
|
|