管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。* Z) I4 g( t( e7 ^" r
6 N4 r t4 i5 Y
u+ X& a# G9 L7 {! Asocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 r: \( H2 m' n& i- ]' L& K* o
' k7 ^$ K2 ?3 q6 ?3 `4 t) z+ w: W: r# d! @2 G
TCP协议
1 I& z" J3 X- qTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。6 ~: Y: f# |+ `, [0 k
. b" m% S0 h! A
4 {, ^3 ^- S( \ O1 D关键词:三次握手,可靠,基于字节流。
% {" g% h; [6 H& e, }5 O
) a0 I! {; w2 k9 ]8 r7 ~" Q2 c, S- \8 ?- p4 x: g
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。' e: n* O& Z0 w) C$ I
8 v/ U2 \4 Y. |; I7 A
TCP服务器端和客户端的运行流程" ]$ T- ^) e9 b/ A8 ~
: p" `- C* ]3 E- @7 [" K4 Z5 W# @
: _6 u, b# N4 R4 v& x, a如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
$ \) }0 g1 a0 x3 B) D* c* ~, }7 i5 y6 I" s
, L' E9 h+ t( G q7 e
1.创建socket
0 Y+ W0 R0 m: | socket是一个结构体,被创建在内核中' q4 ^, B0 r0 s+ q# I
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
6 g3 C4 G7 u% |8 ^/ ^4 x0 x& t* p" p* l
: ^) w% V, p/ e, L5 Y
2.调用bind函数
! \) j7 b: n0 o2 z: k' i- G 将socket和地址(包括ip、port)绑定。
3 @7 x4 z) k% w6 C3 W 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序; R e& {8 ~% y; c3 D
struct sockaddr_in myaddr; //地址结构体; o- x d( i2 y2 |4 M1 [) t( [
bind函数4 e+ [0 K( ~# t4 V
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))0 K0 X- x; ^, Q- m; q
' H5 ^. j* b" Y4 y7 J3 ]
( h5 z4 C j) E8 A: C' x3.listen监听,将接收到的客户端连接放入队列
( K! s) R a% R, y1 n# B8 r listen(sockfd,8) //第二个参数是队列长度. [# J4 d: I" q; H, C
+ L4 x1 e) [; [0 O7 {0 W' K% b
- v$ ~9 v/ c* E; |$ M1 ?4.调用accept函数,从队列获取请求,返回socket描 述符
0 q- b$ W" u! E! U2 Q 如果无请求,将会阻塞,直到获得连接0 m, E s/ Z+ ^$ w d
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
$ v+ I' ]6 l+ ]8 r
9 m7 r) N! U' K2 e8 n" f3 o( M. g; `3 S& ~" U1 o! U
5.调用read/write进行双向通信
: r- L' l6 F; w D% R! W
- e& m& L" r: B4 u( v
" {# U) B" C. x; Y9 R. L# f8 ?# ~6.关闭accept返回的socket
$ X# M+ X! Q$ p7 m; f9 p& ^ close(scokfd);
# p0 A* E' x1 u; L I; m
# ^. ~3 k+ z1 L5 `3 C, K3 @- {5 A$ B3 n( D, C% I# {3 ^; ?
0 `& i, w/ z$ k* b2 W0 F
$ ?' s0 z2 M! ~; a
下面放出完整代码
$ M; }: [; W0 c9 c# r6 e* S5 @7 E4 [: A; B
- /*服务器*/+ @( E4 {; W8 S, M
- #include <stdio.h>
" E- e- i- s L, n - #include <string.h>
, p# K; k& \. f# @+ w3 L - #include <stdlib.h>
% [; z3 K( g. Y; U3 t4 B" l: X* Q3 G - #include <strings.h>
' _4 G' A1 m6 s2 C1 @( }; A. ? - #include <sys/types.h>' ]7 D* e, k' Z! R4 p" m+ s! S
- #include <sys/socket.h>/ p1 K6 o' n4 l4 p0 R4 a0 F" E. ^
- #include <arpa/inet.h>
, s% i* \' U4 c5 x0 B& W - #include <netinet/in.h>0 {; W2 w) ~/ f
- int main()$ m" A) E$ c5 J) l2 c. ~% ?
- {3 X# W% \; }* c; u
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
! U( C2 n! d: G k3 `4 w& U - if (sockfd < 0)
% y8 J, o8 f! @% f9 t c - {! P" z6 g; t7 r6 B; }" A
- perror("socket");" `7 E$ ?. H0 Q# A
- return -1;( u. [# m b+ Z
- } //创建失败的错误处理+ d# ?! n5 N. X S, R9 H
- printf("socket..............
( z5 k- Q& f8 C) } M - "); //成功则打印“socket。。。。”
0 X% `6 b! Y: ?5 r1 R1 J* [ - f( m6 M6 l- c
- struct sockaddr_in myaddr; //创建“我的地址”结构体$ d6 n7 B/ l; \0 Q7 e, V/ J/ f. R
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
) l$ n' D+ d' y, G - myaddr.sin_family = AF_INET; //选择IPV4地址类型; `, f( I. u/ P4 g: N, v
- myaddr.sin_port = htons(8888); //选择端口号
; G8 ?- c/ e1 x- x& a4 T7 w! C2 Y - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址& `' T6 _7 ]4 z' C; Q0 C( p
- 7 E! M$ d+ d3 @2 H
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
3 J7 G6 A" O' G( z" U: O d2 o: | - {
* W6 ^ s4 o5 ^- e - perror("bind");( b: c" w) G9 @! u
- return -1;& V+ }0 I1 R+ J( c1 U
- }! D- v3 Q' Z2 ^) Q/ U2 D$ m
- printf("bind..........
% ~# P% w. N7 _0 k1 o3 e: o+ E! x: g - ");
3 s2 n2 p0 V! @9 X& L -
" r$ C+ ?' B6 V - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
7 b Z& z. Z9 N; e9 E1 B8 F8 y - {
3 L$ D. Y7 D7 b6 k, \' d( G3 [- y - perror("listen");
/ W8 ^3 b2 n0 Q. K: z - return -1;
) P+ ~0 n4 U0 X - }
% R3 V& q9 r" ]9 n$ c/ ? - printf("listen............
! H, U0 _7 r0 g" h. q - ");
. S' v; e; T8 H1 T8 B, G - + l1 ~/ q7 q9 z; \; d0 c& J
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
4 _1 R/ Z/ q, X - if (connfd < 0)
. s1 O1 X. B1 _ - {' j2 I4 m3 Q2 b- u* }9 O: u# x
- perror("accept");. n/ I) p) q; c) J" q# A2 d
- return -1;$ e7 G+ |0 B' |6 a8 Z9 ]1 u" g, [
- }1 `3 g! M! S0 O
- printf("accept..............
' f# }" V* C, }7 a, k - ");
. v; W j0 ^+ v: p - char buf[100];//定义一个数组用来存储接收到的数据' ~7 Q# U/ H+ {$ d/ a# x6 k
- int ret;
, q/ s0 S& t1 X! E7 @1 y - while (1). |6 s- z, K; u6 d' M8 d& s+ O( K
- {
5 z* Q5 B8 A* k8 ]. t% k - memset(buf, 0, sizeof(buf));! a N1 u$ b2 Q8 x6 w4 d
- ret = read(connfd, buf, sizeof(buf));
3 X9 x) W$ M# D. t) H, L* `( ~ - if (0 > ret)9 I' x( m- L2 @7 N- w* Z1 H
- {
$ Z: n6 b: r7 H6 F: B - perror("read");
1 C) n2 |1 Y* P& |# D7 x& ~ - break;
& a% M: Z9 R+ m+ D, W, g - }//执行while循环读取数据,当" a/ S6 J. N# T6 J7 x
- else if (0 == ret)
5 p0 X2 |, a d* h - {
9 E4 b6 Y4 ]1 \9 N. A - printf("write close!
& O7 w. C# A/ d" z - ");9 R3 l5 W6 Y, v; G) l
- break;$ b) U% r3 t5 p
- }
1 K( r* V. F" P+ \ - printf("recv: ");
2 E3 ?; ^. y* z5 x k# M V - fputs(buf, stdout);//打印接收到的数据
1 Q- N% A# G6 j$ Y5 e- v - }, }( v" b1 p8 G3 p/ g0 [( D
- close(sockfd);//关闭套接字
% R4 c& f0 g' J% M# @; V' E+ Q, e - close(connfd);//断开连接
$ S) J: k- b2 q6 J* B6 ?0 x( | - return 0;
+ A' `. k2 C5 K" I* m ~ - }
复制代码
5 @+ D2 u- E0 Z e
1 G! |% @: o' s4 Y' K* h+ L0 A- /*客户端*/(具体功能和服务器一样,所以不再加注释)
: _# I& W$ [# } G% b - #include <stdio.h>$ L6 J# U* |! ?& E
- #include <string.h>3 F8 a+ B" X& Y/ V
- #include <stdlib.h>& D* @: _: e* _
- #include <strings.h>6 N6 L$ Z% G6 z$ s
- #include <sys/types.h>9 P5 G& ?9 {$ M% j
- #include <sys/socket.h>
! ?$ @4 x) h0 ?8 l( D# g' V - #include <netinet/in.h>& w, _. K5 A1 e+ N; c4 s) E. T
- #include <arpa/inet.h>
4 c* U$ P0 E* }3 P4 w c$ B9 V - int main()
# v% k+ `, Q7 X9 `2 r( ~1 a - {0 I, A0 Q# Y0 U2 ^* x) u
- int sockfd;
: R/ u% e, v1 p8 y: }$ } - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))6 y; |9 S* k% X2 m f7 w
- {
% M( f! n9 Q+ ~6 e# O - perror("socket");
9 U" A* h1 J4 m5 j2 ?, v L# S - return -1;: |# r- w- P& C5 L
- }
, f4 f$ R; ?; n% ]) j; i! |7 g - printf("socket..........." S& _; w& h1 ?
- ");5 y9 Q& m1 _0 k. } w( x0 t
-
3 d L. }9 M3 z/ D - struct sockaddr_in srv_addr;% ~2 E- ]) f* J2 C
- memset(&srv_addr, 0, sizeof(srv_addr));& _* U' ]7 l5 E. \2 `# ]; h5 R& }& f1 C
- srv_addr.sin_family = AF_INET;* n8 R1 `. n9 P3 q
- srv_addr.sin_port = htons(8888);
. [" j- K/ S' `, t' s- p9 c* k - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169"); B6 w% [1 R! R% `9 p8 l
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))2 ?) Z' C$ D1 ?: c/ y! \6 L; \( O: s
- {2 r: }7 n8 P: {+ |2 u% [0 P6 j
- perror("connect");/ l) r& O9 A: g; S, X7 Q3 @3 C
- return -1; //exit //pthread_exit+ j- b. s/ ^, y2 G& u- ~
- }/ c3 M; E, O/ _1 k( _2 Q- D7 l0 Q3 Q
- printf("connect..............
0 |- M7 [0 p' f: X - ");
+ Z+ `$ U' K4 E1 D- S+ c - char buf[100];
, q) u! d$ M% H, w2 d* }% V& P - int ret;4 u9 e) c! @! M$ e5 I: D
- while (1)
% e! W! j/ ~1 C/ u9 L/ E* Z - {
$ j0 M. m$ F& k8 J8 @ - printf("send: ");
$ J( z# K" P! Z; L! U7 p - fgets(buf, sizeof(buf), stdin);3 z& y6 O- R5 Q$ S5 _$ T
- ret = write(sockfd, buf, sizeof(buf));
! m2 w; }6 g& a/ V% V - if (ret < 0), m3 k; [5 ^) i
- {" Y l m- O& ~( C
- perror("write");; j/ {0 M! } z/ y3 I
- break;2 ?' S! s5 f7 M* Q2 m# w
- }
8 |" L, r3 q) H5 k: o - if (strncmp(buf, "quit", 4) == 0)
3 L4 j; K5 c; r: x& d - break;) w3 ~+ `2 C$ ?/ F
- }) ?' ^9 B) [: \7 c) @
- close(sockfd);
/ i% a" o3 }& _" Y$ M& y+ i9 a- b - return 0;
7 a2 o' `# c, p2 {: E- f - }
复制代码 ) m* C) w2 x+ v2 M, j
! y% ~, {+ O3 i0 q. ? |
|