管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
; A( l5 @+ g, _; R' y) w5 e1 C+ a; G' C7 n- Y5 o/ S
! ]5 g- M4 D/ c
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。; B# b5 ~) ~! _* W6 L% T5 [
# I0 T# |8 `/ ~4 |6 c" F4 Z, R1 e
' U- k. q* l, e5 Q2 W, `' _TCP协议% W" l9 }& S, }3 F
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。, s5 F, Q" u- D8 M
9 }: G3 U; t" ?; M- x
; V, M: U9 w" t# q/ F8 A$ i+ q
关键词:三次握手,可靠,基于字节流。* `9 F; |# y. h
2 i6 e! I9 e) x
! c$ ?. F2 \; [
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
5 Y; S) P$ D8 k6 d6 l* b0 o8 J
$ T2 n" c, ~5 r b- K' I
TCP服务器端和客户端的运行流程5 x* y' F7 S$ t- _! N
7 E# L" H# j4 ?2 [% `9 j, ^; }
: I, K% S# K+ @; Q2 x3 a如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
: J A" u1 ^9 E$ y9 j+ S! m$ I3 P9 c8 u$ L; g6 ^6 f7 H) _3 C/ h
0 |1 v" j7 \) g/ h1 b1.创建socket: N9 C1 K: d, u
socket是一个结构体,被创建在内核中
7 U' i2 K$ S6 p4 _3 M sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 B/ A# @+ T- w) x$ Y' c* m6 g. H# ?6 ?% w
. l' l S0 H. m2.调用bind函数
7 k& w A5 K/ C3 R1 M5 S8 D- D 将socket和地址(包括ip、port)绑定。" | T0 X, }6 L3 m0 K; K
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
5 x) X3 X( {2 ]5 [7 O' G+ W, s struct sockaddr_in myaddr; //地址结构体
; s6 }- j5 i0 ?! z bind函数9 I+ ^8 G n& q7 t; n+ B3 } `
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% t; Z+ R5 j0 \
+ ?6 {+ y, c4 Q
* o& D9 p6 ^& D$ [
3.listen监听,将接收到的客户端连接放入队列
7 n8 L7 g( M, j r4 | listen(sockfd,8) //第二个参数是队列长度
4 M. P. }4 U: O b' U: u9 h6 R% I4 R a2 ?3 q1 R
& o6 F) C% v* S4 p% l4 T
4.调用accept函数,从队列获取请求,返回socket描 述符; W9 `5 k# ?$ \+ `$ J
如果无请求,将会阻塞,直到获得连接
" U# t% a" z! u( v) E( @, B1 i K6 D int fd=accept(sockfd, NULL,NULL);//这边采用默认参数2 M$ D# T) {8 g, _% q5 e- \
F$ e, E! {# U; L. n6 U/ X9 V! p g7 v! c& N8 P2 N. v$ O9 L
5.调用read/write进行双向通信
) C6 s& x: @ E5 P" V/ z' m' Q0 @3 ]& Z# q: h& I
+ \' g0 T2 m1 G; `& L6.关闭accept返回的socket
0 l( k. ?; D, \1 P5 z close(scokfd);
4 f6 @6 ?9 o5 b- {5 c9 q, J b' H, C, ^
4 ]% t2 B* K% l6 s- F5 {
" i f7 g* d9 O1 N
* C' `, Q2 \ _0 w$ H& [3 ~. P4 N下面放出完整代码# T! W, `+ K8 |& ^* A `
6 E! L* M4 @5 c/ o$ D- /*服务器*/
6 q6 ~ f5 g/ c% Z - #include <stdio.h>
* E% y% ?/ s! @" Q0 M. V - #include <string.h>' E/ \5 h% N# f6 Y5 q
- #include <stdlib.h>
4 _/ K7 a! v% } e - #include <strings.h>
$ l, g$ R2 J5 T/ c7 q+ H - #include <sys/types.h>$ O ]: Y- e5 N) u) G X
- #include <sys/socket.h>
) v9 Z, M3 }9 D* v, o" t - #include <arpa/inet.h>+ o+ d% o8 A* w0 G) d z$ r
- #include <netinet/in.h>
' y" B: T8 P+ x1 u8 D; W - int main()
1 ~0 O' L; [ F9 m% X I0 T - {
7 J0 O2 j) O- s" k+ U+ A S- t - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- ]9 y2 g m F
- if (sockfd < 0)
& x+ }4 b5 T# p5 j - {0 ^) W& S4 W5 E( v6 u
- perror("socket");- C6 x, L' c4 V U$ M( G0 v
- return -1;2 T" C% A* u1 s+ Q5 {( o
- } //创建失败的错误处理3 [0 L9 s7 J* n
- printf("socket..............3 k: ~+ W" x7 R$ M6 Q6 i
- "); //成功则打印“socket。。。。”2 q/ @( \* }$ D' t
- 1 E/ j- x; y5 k
- struct sockaddr_in myaddr; //创建“我的地址”结构体
2 Y" H0 H) i# \+ H - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
( h1 F8 J8 B( c - myaddr.sin_family = AF_INET; //选择IPV4地址类型
" w- L$ z' o, U2 L - myaddr.sin_port = htons(8888); //选择端口号; `8 K3 K, m6 p8 H8 Z% b" l: Y
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址& i3 u4 @, p8 S
- / S0 |' L% N% D
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
9 S# M. ^# ?/ e6 c6 v% o) G! v - {4 { I7 x- p4 s% E( M
- perror("bind");
9 K* W/ ]: W+ C, m, [ - return -1;
/ l; L7 u) ^0 C! I/ c - }
( u/ ^. b, L# V - printf("bind.......... D6 e2 ^0 ~- W0 r0 a; [# g0 c
- ");
5 H; ]4 y' V7 d( W - : W% A# T6 g# O3 t, Z
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
' j% w! [: d0 e - {
+ k% z: e* S2 r3 J6 Q T% E2 s7 n - perror("listen");. X1 D5 r, g' [' F
- return -1;" \1 `8 i2 E8 s) c# m. e) G0 P ]
- }: x& w+ T. w8 ?- ]' u5 Q+ i' u
- printf("listen............4 S8 n u3 z7 k U; j7 r4 V
- ");9 w8 ?: [- i: N: m
- , z) r8 l1 A: W
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
2 C" R, P1 s q$ ]" \& s4 E! K - if (connfd < 0)
( F% ]* s t( b# |8 s( X - {
( v j# K$ L7 d8 ] - perror("accept");
1 R: {) Q. ]! p& H. ?$ v - return -1;
, |0 W7 B% f* C( o, K @9 a* A2 [ - }
) q9 U! q: M9 R W - printf("accept..............
/ ~* Z& L" z" x0 B m% E7 [( t9 H - ");
, M8 F- x% U' @" {6 J$ T1 M* v - char buf[100];//定义一个数组用来存储接收到的数据 U# b+ a# X- \' Z9 v- Y+ B
- int ret;
' ~! F* e. `( O S - while (1)/ z& @9 E# [1 L! q4 T3 K
- {* ]- _5 H- m* K9 t( w' f' Y
- memset(buf, 0, sizeof(buf));6 ?, C4 z% y2 n* j9 g9 A1 N
- ret = read(connfd, buf, sizeof(buf));
- K9 e6 |! d0 \: q* Z- l8 I+ s, | - if (0 > ret)
* K5 J5 S! G. \9 Z" s7 l7 u - {
" f7 L* g$ Q }& \ - perror("read");" G4 {+ n+ r0 Y$ e
- break;" ^) O8 P; x) r/ U: E: y$ H, u7 @8 _
- }//执行while循环读取数据,当
2 V( e% ^, N- D p3 [, x9 ] - else if (0 == ret)0 f' P8 u9 L$ M5 g. ~$ |
- {
! `6 M* ?* _. G/ [/ k9 u8 i+ d - printf("write close!
/ r& ^( N, D4 b, L7 K5 f2 o - ");
4 @/ F( t$ w+ R7 U - break;
- Q/ l) |! k" c" i4 [3 `0 u - }
4 }+ z( n6 _. I+ S' q( e2 { S - printf("recv: ");
. h5 z3 t4 h: \9 Y' q3 x - fputs(buf, stdout);//打印接收到的数据* h S# g: B- F
- }& H6 t: y F/ t, K A f" `
- close(sockfd);//关闭套接字
* e+ v& n1 i; p - close(connfd);//断开连接: P# }4 D; i; f. `4 d) C& h8 K
- return 0;
$ R) W" ^6 [/ L; O" A4 D - }
复制代码
& R7 @0 S; z1 o- H) |, h0 w* i6 D8 p8 \( l! _/ R: R6 \
- /*客户端*/(具体功能和服务器一样,所以不再加注释). S2 T4 B" \- q: \; A4 o
- #include <stdio.h>. d K6 v8 z u [8 |
- #include <string.h>
$ p6 Z J0 e- P# y - #include <stdlib.h>
- ?" r( s5 a& w) ]0 A } - #include <strings.h>
- e1 X) r2 x# g6 J/ T, c- { j - #include <sys/types.h>
( `- j @6 e% v5 p! X( b - #include <sys/socket.h>. E1 {4 Z( H3 p4 P' q3 ~8 F- I# D
- #include <netinet/in.h>
+ a5 k1 q) S' v; a& X1 v+ \ - #include <arpa/inet.h>
8 o, Z' S! I4 Q1 C0 W! V0 x9 | - int main()+ { n6 b& Y8 v1 h, U( e- p; W6 ^( ]
- {3 |, w) v4 l5 y& ]
- int sockfd;
% \) C( s7 G: E7 u3 T - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
. b) R, E- u5 u6 e* x# E! m - {
! m$ F4 t- f/ H' M4 T* M6 e - perror("socket");
: G. i+ F v6 x- S# O2 x, q - return -1;
\. ?# q1 M( P) l5 P- D3 S' H - }1 x6 v# j# v& B6 N* M3 }$ T5 E
- printf("socket...........
$ z2 ]9 d: K/ z, u - ");
3 g2 l. ~6 H: ]; e; U -
; v8 \1 M! o3 X - struct sockaddr_in srv_addr;
/ F: k6 Y/ P, H \% p$ F2 r - memset(&srv_addr, 0, sizeof(srv_addr));+ x' R5 S9 V r9 n" c" ~; t2 v
- srv_addr.sin_family = AF_INET;
0 W `0 m+ J$ G$ O - srv_addr.sin_port = htons(8888); J- A8 t" }1 U, O& w
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");% V6 W. w" U' j- V; e+ Y! f6 U4 j8 h
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))), f" _ P% N/ u. k' M3 I+ D; z
- {9 c2 w# F; o# |2 C
- perror("connect");
# L* j) m# X7 ?' q" A+ ^0 A - return -1; //exit //pthread_exit2 l! S: C4 J3 U6 |# J" Q1 R
- }
# a5 L r S8 u% g5 c - printf("connect..............
" x' w# d3 ]8 A& {3 P - ");
1 `2 g3 V/ s1 y+ b0 S7 K) Y - char buf[100];
1 ?) T7 p9 J G; c - int ret;6 ^; o: |# Z3 s8 p2 C
- while (1)( G& h5 v G( D6 u0 u% G! R
- {' [: }( l6 ?4 d5 V: u' c
- printf("send: ");$ n, a/ Y: A' X; ?/ v0 k
- fgets(buf, sizeof(buf), stdin);: h3 ], k) K2 `
- ret = write(sockfd, buf, sizeof(buf));
3 M, {8 s. V2 P0 p l - if (ret < 0)9 Q: _1 f+ y& i. X. u$ S6 j' o( m: z
- {
9 G& d4 |6 [3 W& \ - perror("write");+ H' f9 b, K& h, |3 c
- break;1 E% R/ P" c0 E! y# v
- }
9 U5 T" X1 s( D% ?. t - if (strncmp(buf, "quit", 4) == 0)# ]9 a/ h4 y% s
- break;
; h6 V+ O/ Q# [" ^* n. B2 a6 r - }4 O. \- R& N/ Y N" q% n
- close(sockfd);
' r+ P6 s4 m! p) G) F - return 0;
0 q' S8 G. j- u' B) k1 v0 d - }
复制代码
' `) n2 N, S5 i. E+ d* g' H* }& S" {% I3 |$ v5 Q, q5 W
|
|