管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
8 j* g1 N! }! N' {. P# K
" M6 { i3 H! G4 P1 z
0 x @* k: P8 L$ B' n1 N/ Fsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
) W+ H2 i! Q1 S9 ^- ~4 e8 b4 v2 x) k" N; v9 t$ g: o3 t# z1 g9 j
7 c! n9 v" K" D& c/ R$ c$ |TCP协议
. i% O d- R* j; ?( c- NTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。/ a6 y: D. A+ I4 J" V
$ @& M1 B/ G! s
6 t4 z2 k0 _9 M5 s- G' t7 s3 c关键词:三次握手,可靠,基于字节流。9 O/ A2 u3 l7 z& X+ d
2 C+ i( f. u0 Z) u m% W4 T- a/ W- l& o5 B& b4 B6 C
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" Z4 Y+ y/ Y% B7 f5 H+ h( q
o1 |& p/ G) ^- w! t8 q% C8 U4 s% ^
TCP服务器端和客户端的运行流程
, M1 W( {+ E+ ], V0 U) D5 |* J' s! t9 `) v5 X% F) }7 O# f( ?; D
0 T7 M t% t9 f& `
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?3 Y" n8 n! p+ v- @
" b# w C7 v3 i+ X l$ n
# J# d3 A& x% r' A; p7 {
1.创建socket
2 I. `# u% g+ _( m socket是一个结构体,被创建在内核中4 Z4 T3 {* _1 |) f3 I, z. |
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议4 E( M3 u. B. x# G# G
! S1 s% i0 @8 T7 w7 ^. c
9 [" o( p+ I: ~) H% w2.调用bind函数- R# F) C. O2 U- e6 `# i- b
将socket和地址(包括ip、port)绑定。
8 j6 g6 M5 g' l! C* n% B 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
4 L1 K7 @, D+ Z5 |: P6 { struct sockaddr_in myaddr; //地址结构体
5 _' ~: |0 b3 l& B/ L# a bind函数
/ a R8 c" Z, \: S, m5 Q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)), \% U* b, l9 i1 M' L, E2 V
6 o; @; Y; Z: V9 |" A7 c- t% ?
; m: t" o6 g, T+ y
3.listen监听,将接收到的客户端连接放入队列' g( C& `9 @! z! _7 b3 ]
listen(sockfd,8) //第二个参数是队列长度: e& o, V# e e
# e3 h- W5 }, p" N+ I8 o8 H# T( Q* F1 O$ U/ t4 y2 C' B1 i" W
4.调用accept函数,从队列获取请求,返回socket描 述符
0 M' C3 z3 g+ k/ Z6 F. @% r 如果无请求,将会阻塞,直到获得连接3 V8 Q" ~. X6 B6 V
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* Y; E/ \1 m0 b
# g) ?+ J; u9 y$ L: r6 f Z) V6 Y7 B
5.调用read/write进行双向通信7 A! @3 u# t6 g7 R8 W$ o0 C( h
7 k3 Y( u! p/ M/ B; p% g
6 h, [7 k; o1 D# u" q# K6 ?# o: @2 e6.关闭accept返回的socket$ x0 v( c$ j# K; y4 ]7 L' R, r
close(scokfd);% t" ]. Z' O4 z: ?0 W# S; r2 m
) y0 c8 K) c. w! O8 l/ C
/ y: N k! ?" N* }; v
( z: }0 Y; U4 s: F5 k7 C. P6 @ J4 H
下面放出完整代码/ B0 W# ]+ v- s( d
( h4 o5 ]) B4 r3 C2 y
- /*服务器*/" _4 W9 M) N4 C' v* G0 O5 e
- #include <stdio.h>
( m2 O, ]6 u( x. ]0 X& h" t - #include <string.h>
2 A' p0 t2 \7 f( O7 \ T: g - #include <stdlib.h>4 L( i8 Z& y/ y$ V
- #include <strings.h>( V' }5 c K; h; L/ S( V
- #include <sys/types.h>$ K; t& [4 n+ x7 D6 m" n
- #include <sys/socket.h>
- z8 r- V2 Y7 `. M3 |) g - #include <arpa/inet.h>" i. W# A3 z* ]6 j9 ^
- #include <netinet/in.h>
, j6 Z" r/ x3 w- S - int main(), L/ t" D2 i5 H
- {
" {9 k8 }; n! L, e, L2 g - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
8 [- M$ M, z+ M& H$ |( i& E8 r - if (sockfd < 0)
9 |, ~4 W" Y# j+ y: y' E6 @/ D - {
9 C J! N4 Z0 S9 i9 H- T - perror("socket");
1 _3 _# q+ [+ D. m: f$ w - return -1;
9 S3 i V0 s4 b* t - } //创建失败的错误处理
" H: l% G' H2 R! @$ k - printf("socket..............9 [, \/ ^' n( b+ W, [+ \+ C
- "); //成功则打印“socket。。。。”1 i4 {" ^2 U3 ?
- - w( w4 P+ g- \5 x
- struct sockaddr_in myaddr; //创建“我的地址”结构体
6 |. m# Q$ y. B( @! [ - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
1 _8 G; p) }$ d% P) n - myaddr.sin_family = AF_INET; //选择IPV4地址类型8 t6 [- h# U& d6 L: j+ w4 J5 `
- myaddr.sin_port = htons(8888); //选择端口号/ c+ G; x5 d* t/ g6 ?" I- B
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址& G1 }, O' T! k8 @
- 4 m* R# i! v: y
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 ~" v1 R, L% i8 j4 H! _# e* A
- {
0 K9 O0 \3 {$ o7 A# p8 S" b - perror("bind");" Q# U' N5 B0 U7 j
- return -1;) q" F* i5 j" z# t0 x5 g! G9 i
- }
& i t$ Z9 }4 ]* Z" q - printf("bind........../ T% u2 M4 ] x0 `% q7 C* i3 j R
- ");
1 T, J! g0 }) r9 n: q9 ] - * y6 x5 K/ w1 ~9 w0 `
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
: l0 u/ d. V6 N% l" W2 V - {
" z1 E8 M" |6 l+ ~2 P' v - perror("listen");
. u$ {9 [1 _3 m! c9 p8 _9 ?' O: q! _ - return -1;
?' T5 N% o' x1 W: k% k - }
* p- q' [6 Y: ]9 V - printf("listen............
% t: D; x+ G3 ?3 ? - ");$ d' `6 U4 {9 P- T& D
- . S1 V/ o+ q5 ~! [) T
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
1 Z; v6 k! B0 Z0 ? - if (connfd < 0)
/ \' C7 B P; j: }9 u - {7 l. _, C% w9 h: n
- perror("accept");9 H* S" I& n7 J0 H+ Y6 B+ @. q
- return -1;
5 P* D- X* I/ n% a& @ - }
/ j! a0 x0 k# o$ l - printf("accept.............." I# O/ ]5 N, H: ^
- ");( Y1 r- Z! F3 E. W# P
- char buf[100];//定义一个数组用来存储接收到的数据
# e X: i9 f {2 t; ]8 h$ Y* u - int ret;
0 }+ B0 \# z0 l# ~$ ]! O& S$ ^ - while (1)
) m( @% ~ Q; f6 a0 J - {
6 L4 O+ |: |% Y2 D - memset(buf, 0, sizeof(buf));
5 L! X* G; C! |/ ~( v - ret = read(connfd, buf, sizeof(buf));! a2 v5 `5 I0 t# _# d
- if (0 > ret)" [! x' B) s8 q( Q2 d) r( g
- {* j1 c3 o- D% ?( k2 T; [" v
- perror("read");! {4 J. h$ t. M" J7 J7 `! H) W/ w# ]7 w
- break;
: g2 l- O) A1 r5 e - }//执行while循环读取数据,当
9 p( ?+ G+ O6 o& }( R - else if (0 == ret)
1 B5 {. S7 {/ y( J - {7 r# g" L/ H5 A! R' n) G
- printf("write close!
9 w. z- w! I9 a9 Z - ");
: c& D& T7 J9 |! a- [8 x2 T" u - break;
$ c! ~0 c) l/ S2 X* v T - }
4 N) v/ z; |9 j% T5 n& w# w - printf("recv: ");
# ^& C3 M2 ?( b, e* J - fputs(buf, stdout);//打印接收到的数据# m: ~- k; e' \! O* c3 q2 d. l
- }8 L/ m! q" f. K$ N8 u$ {3 p
- close(sockfd);//关闭套接字: V# d& g9 d. L2 ?0 S5 M
- close(connfd);//断开连接* K/ Q1 P9 O; ]/ a
- return 0;, y! E3 W. r7 }1 p3 Z& N* o6 ]/ y% C
- }
复制代码
& _5 S0 {- a- r4 q/ v/ E6 F3 D, y: h, i
- /*客户端*/(具体功能和服务器一样,所以不再加注释)5 ?. T; R# ~$ x4 ]
- #include <stdio.h>1 P4 U- r/ K% ]. ]! g/ ]& E1 L8 e8 f j
- #include <string.h>3 I$ q) z- c3 Q/ v
- #include <stdlib.h>" X& k3 N- ^4 o! D- a- `, \
- #include <strings.h>+ s- U7 J) U( _
- #include <sys/types.h>
f5 l- p. F2 F - #include <sys/socket.h>3 q* X& q( M0 e8 }1 l
- #include <netinet/in.h>' M- ]9 |! s' i, C& W3 w6 q) k
- #include <arpa/inet.h>
* [, U) W6 i' n6 ~6 K2 [3 M - int main()
* F& [$ o d m9 o- v - {
, Q {* \, O( c& C7 I" p - int sockfd;6 N* l' F! ~. ?; k! }2 p2 v* f; G! \, \
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) H1 p0 k& `1 v7 x5 r) e( C; H( W3 w
- {7 y' L& i. z3 H3 x$ y# F
- perror("socket");1 ?( X& m! R; d, `; q- n$ a
- return -1;
^; ~6 ?! ]( I9 ]2 M - }
4 L# J4 l; \6 z6 g+ E% C0 T7 |; u - printf("socket...........
; a* L, f8 W; z) ? ~- r - ");
5 J4 c2 E9 t, L5 \+ o4 D - - b4 j [$ I v9 v
- struct sockaddr_in srv_addr;
1 E1 E! o+ ?# N# o# T - memset(&srv_addr, 0, sizeof(srv_addr));
Z: K# V" z6 D/ ?5 e' o% b - srv_addr.sin_family = AF_INET;
; |0 Z5 \/ m2 e! o3 y - srv_addr.sin_port = htons(8888);
! X$ ]& h: l l: Y - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
7 ?& E8 ]2 m- I! o' C5 Q5 _ - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))' R. `1 \& k7 q! Y8 s* ]: }
- {
& y) i c4 H# @0 ]1 @ ^# X# T - perror("connect");6 g8 ]$ y3 V1 r9 d" A, I! S
- return -1; //exit //pthread_exit+ _& A# `1 `3 ^% V- p; I
- }
+ A' n" H/ O7 f% w - printf("connect..............4 F g1 U+ m- E' N0 ]0 B
- ");& f: N1 B: C5 Q3 V( i* j
- char buf[100];# ^8 s& n7 o, d7 T8 T% Y6 r- w3 s
- int ret;5 R( `* s. r- m" K) I
- while (1)
8 G y) z$ a; K6 L. u4 C. @ - {
# W5 ~- C5 D6 u4 \ - printf("send: ");: ^8 n+ t- |$ a' u
- fgets(buf, sizeof(buf), stdin); P9 E5 j8 I8 R, Z' J. i) u% r
- ret = write(sockfd, buf, sizeof(buf));
4 m4 i" I$ V8 @+ q - if (ret < 0)
" j6 W4 y" f& d' V j# r$ v* F - {. U' `; R6 g4 t0 C4 d5 l! Y% U' [
- perror("write");
8 W0 H! u5 k- y. {* U2 Z - break;' P/ N- X: O% \7 v/ X: M3 {
- } Y% k; b: V- G6 ?1 Z5 V5 o
- if (strncmp(buf, "quit", 4) == 0)0 x( p7 G( h8 a3 C
- break;; Z3 t5 [( I# `6 g- A+ R! K a5 E
- }
! I, Y6 J$ @6 V6 r$ p' c3 M3 p - close(sockfd);* {' t" A' [9 R7 f+ J0 j
- return 0;
5 u8 p7 {: d5 y. W) b6 X - }
复制代码 8 G5 E- C0 e0 ~, L+ v' |: `
* E. e# R+ Y! w; |) n7 c |
|