管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。+ |( z' w) N9 J; t( q( H
8 y! B6 ^3 z" s, U
' h. _" M& C% T5 Ysocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
) p7 G" v9 V' O8 c7 @' a( }: {
" E' Y$ R+ u! _. R9 `0 X
; U2 o4 G8 l/ O8 d! _2 kTCP协议1 H! m, W2 ~. @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。7 O" O5 c# ~: c
% l2 K) J9 T1 D! i1 R7 K
) M3 u& E4 o8 B3 F1 I3 b* \+ x
关键词:三次握手,可靠,基于字节流。
& ]$ p5 S* v2 }% W8 w
% E7 D! J0 J F+ J, B; c& t# m1 P% y3 v y$ J& q0 G
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。( R4 Y) o$ s4 _9 V" `! a/ _) ?
5 d8 b3 D3 L" G: MTCP服务器端和客户端的运行流程) ?3 {% Q4 ^% ^' x/ v% @5 s
. `" d& M$ G1 Z T* [7 r. B7 W( l; c$ w
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?! m7 A. X& ?0 C7 f9 `5 i
4 E1 p2 B8 |. f$ G
2 B7 c9 V" `# E W8 D% d1.创建socket
' \. Q7 J& Q- D8 M socket是一个结构体,被创建在内核中" @( \4 y! r+ e3 ^1 a- h
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议# G. d$ w6 L/ ~6 ?; m
( S4 [3 x0 y9 b
8 E6 A; o/ N% u* p4 q6 c8 d
2.调用bind函数! g( a5 r1 ~/ L: L6 ^. m D9 v
将socket和地址(包括ip、port)绑定。
( e/ t' K$ @1 m4 k8 H 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序3 U. @" i* ~( j" S4 E' [ z7 \
struct sockaddr_in myaddr; //地址结构体" D9 d' C9 h* p2 v7 ?: X: t' U
bind函数1 T! [! E+ \$ U
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% c8 V5 ?8 T% r& c' C3 w1 n
9 l$ ~$ H& t) g% \. L9 b! Q
8 s3 u$ D; l# n
3.listen监听,将接收到的客户端连接放入队列
9 \, @) b+ x3 M* a% d$ w% \ listen(sockfd,8) //第二个参数是队列长度 i7 ?# ]: X$ h, l* }' K
P; u9 b- n+ S% d3 \
; B+ Z. F: Z: S1 ?' U4.调用accept函数,从队列获取请求,返回socket描 述符. W# k3 d: `% ^; M2 i
如果无请求,将会阻塞,直到获得连接) q% }& g1 P. {1 V9 E2 f
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
! K; k j2 h! A! u! G# D
3 i- y: e$ E6 Y1 [+ g" p; O+ Z+ y* f: g- \
5.调用read/write进行双向通信9 D' j q: F4 x N9 M, K5 q! e
$ O7 O6 j+ C U' H- h9 Z% d8 t
9 V3 m( e1 w) p" O$ c7 R
6.关闭accept返回的socket) @* _) P' z! v3 b0 L1 D
close(scokfd);
2 N2 H s6 x+ c4 c' Z) Q
8 ]9 D. J% E- j" a- F
- J- }$ s$ P# }% I M, F8 F& p& {1 Z; i) p# H4 i$ U7 r. b
; @0 r! h8 X$ r) T7 o( k下面放出完整代码5 [+ g9 d8 H* t/ F! [* J5 g8 c
+ _# f/ M4 G$ X$ j/ o- /*服务器*/3 y8 t8 f$ G6 l# l; h F5 C
- #include <stdio.h>
& L, d! f5 d7 _4 x9 y" v' m - #include <string.h>
% B! ]; ~0 w* g5 p- A - #include <stdlib.h>- V! a9 p: S a, q" n6 T
- #include <strings.h>: }5 P7 i J7 |4 ?: l& M2 t
- #include <sys/types.h>* J3 [) p) p8 j* s' t1 ?% E4 G# k
- #include <sys/socket.h>$ G1 p. P/ k5 V$ j! f
- #include <arpa/inet.h>
" c/ h' k) A8 Z1 X+ D - #include <netinet/in.h>9 t& {+ }4 @+ r2 o5 [
- int main()
3 O) [: e8 v' F* X* v( R2 v9 w0 e - {
0 c) G2 H" i9 l/ r+ V' p - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字6 Y9 V" z, p. a7 S1 K; r
- if (sockfd < 0)+ f7 a# _3 j3 w; G
- {
: ^! a% G- Y/ c5 T - perror("socket");- I; w" A/ M% K9 f$ O& f, ?
- return -1;
* c4 U6 i9 c6 s& a2 r7 M9 z - } //创建失败的错误处理
8 ?/ e$ |3 `4 X4 r( ?4 d* K5 o4 J - printf("socket..............% @( [% I! B" V
- "); //成功则打印“socket。。。。”% m3 N# E% y/ \/ s; L: R! w9 o
- " _( ?2 F4 Q: n) L6 _) J) G
- struct sockaddr_in myaddr; //创建“我的地址”结构体
! q- T% o7 s; E. g% L( m+ N - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)* `& s' E, o9 I
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
# o# R4 _) }" H7 |! m0 D - myaddr.sin_port = htons(8888); //选择端口号- j; K4 q. |4 J# ?. `, h
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
- G( T8 @! Y. s, |7 b' x' l: B* {% M8 a0 ? - ! }" B2 Y4 V5 x& ]0 c
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字- f) O" `( j' d3 |7 }
- {3 _. W) _( s; ~7 h
- perror("bind");8 J0 Y" \' m% p d! A' l
- return -1;
j' d1 `, a- {" ^9 R/ T( N' c - }
7 `4 s( [* Q" Q' ~2 B" ^1 q - printf("bind..........& q# t# H, h/ |+ I4 r1 X& d$ e
- ");' C8 R L& @5 n0 o0 ~; G0 i
-
6 e1 y0 `, Q- k3 w9 G1 g) A - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
2 O6 [9 b5 m7 Y( s - {# n n" S; I+ {6 M9 i9 B4 d* s& W
- perror("listen");* e, u; |( H$ _9 D
- return -1;' w$ H M" R% _
- }
5 e- I0 E; o0 H! s: {; k9 q. r( | - printf("listen............5 E4 L; M6 \' c) C" V7 z
- ");6 @; `6 P p6 O9 s
- ! b0 b- e i. C
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
& t& D1 [2 C0 W# {2 o2 D9 e4 P - if (connfd < 0)
* m- B0 E# T k+ v# m - {
/ g( ~( A. @2 R) \1 T7 f - perror("accept");% t1 }4 A" N6 k. L7 ? x
- return -1;- R7 ^5 Q& Z" v* t( l
- }
% f2 |. U5 C Y - printf("accept..............
( X1 m/ W' Y- F: N; H' ^3 [) \0 j2 L - ");9 N _9 m/ G& }) E
- char buf[100];//定义一个数组用来存储接收到的数据
. n- P0 J. R% S2 s& V2 W# w) p - int ret;
g, C$ c" w# ~) G - while (1)
% @' \; V; G! q$ G+ ?' e- f0 Q k - {% x; x$ c8 U" I& y
- memset(buf, 0, sizeof(buf));" ?, M% B. w) ^4 Z" s
- ret = read(connfd, buf, sizeof(buf));4 i9 j9 G: L' i$ q
- if (0 > ret)
3 ?* F& S8 o% d: j. Z# h! T - {
2 v/ j% C j2 Y. w. u- N# u: x/ p - perror("read");
! w5 c; l& R# a# S7 X6 R - break;- Y0 C: x& G- q9 F2 A
- }//执行while循环读取数据,当
* f! O' Z# C3 @6 w) n6 H - else if (0 == ret)
, }5 e9 _* Y/ M1 T+ @+ f# ^4 | - {8 s/ u, Q* g% D. X# _: \6 c
- printf("write close!; P1 ~( C- C3 U' P4 l
- ");, ]4 ]: ]: D) h' Z9 B
- break;
9 `2 J% l" l9 o6 y1 ?4 D - }
* v8 p1 w6 X. K6 v% V - printf("recv: ");
% M0 M" |3 T6 G, E" s* _3 d- P - fputs(buf, stdout);//打印接收到的数据3 Q$ O4 a1 E2 c/ i/ k. K
- }
8 S9 g) R. C5 r6 a* t. t - close(sockfd);//关闭套接字/ V$ T4 f* ^2 p9 j; S8 R
- close(connfd);//断开连接4 P+ R% o" P1 b( F" X
- return 0;
4 w( q( |9 T8 S2 |& B - }
复制代码 ' @0 b3 B. v3 K ?2 {: [8 S1 {
# e% B/ h. E. u# q- /*客户端*/(具体功能和服务器一样,所以不再加注释)% N. \+ y$ e' _ S0 g
- #include <stdio.h>
# I# [- q1 n" R* h0 P; a o g - #include <string.h>; ] _/ S7 l* m" N& F; G: b
- #include <stdlib.h>
6 V% [6 Y g( P* X1 S9 r; c4 X - #include <strings.h>
& d( J8 n5 F6 u3 t( O8 N' f - #include <sys/types.h># H: W( o7 e; Q9 C; v. l, r
- #include <sys/socket.h>1 M: O+ _6 q N( `. I
- #include <netinet/in.h>
9 x3 U/ D; j7 q$ ] - #include <arpa/inet.h>
; d7 H+ n# ]( ]/ {4 ^ - int main()
( R/ V/ x$ v) ` - {
1 B6 N4 M @0 `! B% E - int sockfd; J+ L! }2 I) t+ x9 q
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
9 J _( Y5 V& l, u8 f- H - {6 O' j3 p7 T, R+ {5 H5 u- Z# g
- perror("socket");
. p- W. N% R* H1 {* |1 A0 D: H+ [: ^0 l& K - return -1;
6 I: e( I v3 F$ D4 O8 v - }% m7 {: e& T0 ^# X
- printf("socket...........
9 b& U% e4 v: s2 ?+ F - ");3 A+ J" m3 C6 }; A
-
, Y% Y/ t/ g) E4 ]( S - struct sockaddr_in srv_addr;' A e9 ^7 Q# p* L- h! Q# i2 h
- memset(&srv_addr, 0, sizeof(srv_addr));/ I, ?7 u( v1 A4 z, w
- srv_addr.sin_family = AF_INET;
, E( T4 d$ p- k: \0 Z. s( X% x - srv_addr.sin_port = htons(8888);' r4 d8 m! v2 i" I2 ~; z
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
X, z7 E! D7 O- I - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))) ]' W: H* p/ H6 B
- {" ^7 c2 |+ v; S' a/ I) y
- perror("connect");
7 G( H% `$ D9 |& T' _- K2 s9 z3 U - return -1; //exit //pthread_exit$ e, n& l- q+ z# x; N
- }
4 Z4 ?2 ~8 C1 r p$ H - printf("connect..............
5 r x9 F! n6 p `: a2 @ - ");
) Q! J5 B. A& y% e1 Z - char buf[100];+ \7 V4 C& O; a5 P
- int ret; r# A# t2 a7 M) b; F
- while (1)( F9 n/ b( [* w
- {
2 A! \ A0 @! X - printf("send: ");
_8 m) c/ `. A7 J6 L, c* ^ - fgets(buf, sizeof(buf), stdin);
+ `4 ^& @/ h- h% M" m; N - ret = write(sockfd, buf, sizeof(buf));
2 T* L) ]* h; `/ m# A( S$ r - if (ret < 0)
0 W0 d4 w/ A* u, K. }$ s - {
! T9 Z4 G$ A7 U ^3 p- s - perror("write");
! [4 D" a& x8 ~2 ^ - break;
# g: \! K3 e9 G: z& X& R; d8 J - }' g( ]; N" ~5 b7 ?0 Z) L- P
- if (strncmp(buf, "quit", 4) == 0)
. b& N0 N% M7 I - break;
; A7 f2 F! z G( ~ - }
1 G4 X' s1 I: j8 l. F! \' N - close(sockfd);
5 Z) i8 d$ l4 _ - return 0; ~+ ]: I# X; J5 Z% Q- Q8 Q
- }
复制代码 + P5 }- @0 H" _! Z
# h+ P2 e5 c& d3 u0 T/ l' T; [
|
|