管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。' G5 m$ e2 Q3 n' X" U; {& k
8 o* ^' C$ S! S" f; d6 i
; B3 k8 K" O8 q9 G; usocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
: } a, y& g/ @. t9 q8 g% r
' u. J7 z: E k/ c" x( z0 O' g
) \: e3 s+ x+ w6 UTCP协议, \, h( b9 l2 n2 m
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- @2 `$ d5 i: ~6 Z7 X
5 Q" N k) }- A* C7 [% ?7 g8 v3 I6 u. m/ @
关键词:三次握手,可靠,基于字节流。8 g5 @8 ^: P' y
$ v/ s3 W) x' W
4 ]5 Q2 j N) v2 K& |" f$ ?可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& \3 t2 G' l p, e8 I
) P4 V" E: ]) w2 t4 _TCP服务器端和客户端的运行流程
3 B8 m" E, Q9 j+ D7 j* o3 z# D+ L
) r, {( Z Q/ {. |+ U9 f$ D& N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ q4 V, u9 S; H& P+ w9 m: I& m
1 b! P% T0 z% [% m, B6 I
1.创建socket, E9 ^' Y; U) g |
socket是一个结构体,被创建在内核中
+ a/ |5 L# D- \: x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
% {! P+ L' n: i8 O/ P% X8 X; v: E& J, m( [7 l( X$ L/ p( F; U% S
: B: _; s, i5 H- B# J5 l
2.调用bind函数3 ~9 f* J- L# x5 N; \
将socket和地址(包括ip、port)绑定。: z% `, G% B }/ i% G5 l
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
& b( t2 c! H3 i9 b% r: l struct sockaddr_in myaddr; //地址结构体0 A9 o: F' j) z# n& J& p
bind函数% c. T1 D0 u) f: U( V4 `- _
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* s2 G2 y% u2 Z( ^7 E2 W
5 ~. `+ _0 _0 F9 ?1 \8 t8 n, A K+ O9 d
3.listen监听,将接收到的客户端连接放入队列- {% V A8 A: Q0 }
listen(sockfd,8) //第二个参数是队列长度
5 p& n/ o$ z2 U! b
( I% B; r; i3 s1 R1 p7 k, r& C" w& \/ {! r& s
4.调用accept函数,从队列获取请求,返回socket描 述符
6 g4 `) d g( K5 C8 A& I 如果无请求,将会阻塞,直到获得连接8 F+ _! p: C4 |+ p- d: V) ]
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 \$ K5 A$ V" k9 B! k7 E/ }8 i* Y R+ ?8 w6 N5 D
% i: N( `$ J: L6 p/ ]
5.调用read/write进行双向通信
* j- L6 T+ j! j; I
# d/ a* k; L0 h8 x6 K; |& q* h2 o6 P7 Y! d, J: a
6.关闭accept返回的socket
3 b/ P4 j; h d. n& O% D* e( D close(scokfd);
8 F* p' c6 y7 R$ @ r
* P, u; V! w1 X$ n8 A) g9 }$ ?/ d, r5 }" ~8 J( B! ]. U3 ~
6 G) b. X& I8 _& M/ b+ ]* M" v9 H/ {% S& e1 c% ~
下面放出完整代码
0 ~4 J) Z8 u- j
/ z+ s, p7 v5 f' t3 s/ K- /*服务器*/
! b0 G+ u* G! L7 ]! J) S - #include <stdio.h>
& C- l; Y- \# w+ b- m, r - #include <string.h>' E/ [: r; n( C* P& l# U2 v1 h
- #include <stdlib.h>
; a1 E/ u- T* ? - #include <strings.h>1 l3 ]4 [9 p; @6 k/ v& K" \
- #include <sys/types.h>! R9 v( {! `) V( l s7 @
- #include <sys/socket.h>
# O! j8 `+ O. _) d- F% d& B% n - #include <arpa/inet.h>
8 x6 C0 ?1 O1 U - #include <netinet/in.h>
* Z" u1 \" o b - int main()1 b7 f4 ~: O! X2 H5 G1 e) ~
- {3 ^- T/ Y$ x0 d" u, t( C/ {1 x9 P
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
* L4 a- L2 `! ` P3 Z7 c* W" \ - if (sockfd < 0). Z/ l2 n* E: D! _
- {: ?5 p2 U6 }3 _6 K
- perror("socket");' w+ m3 t0 E" o3 X/ E
- return -1;' \" N3 W( L' P. {8 N. p, |4 a8 b
- } //创建失败的错误处理
( D& E/ [* y: Z. I - printf("socket..............0 i: L1 N; L8 j/ J V( n4 ?, u
- "); //成功则打印“socket。。。。”
2 \4 f) x3 u- ~' o6 Z) C' {: a - . A% C/ n5 T0 h
- struct sockaddr_in myaddr; //创建“我的地址”结构体- }& M% [$ Z D% |8 w& f
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
5 g' ~8 ]+ A% C# ^) R$ X - myaddr.sin_family = AF_INET; //选择IPV4地址类型
9 I, }7 A9 b! ? - myaddr.sin_port = htons(8888); //选择端口号
; M" u& ^; I3 W4 o1 x l F - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
. a) L2 Z# l8 [$ p -
- ?( k( o- z& W, \+ Q2 R' K2 C" G - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
- ]" A& F. e" c# V7 l/ k' B2 n - {9 y6 E1 W+ W3 \
- perror("bind");8 Z% m: Q) t S
- return -1;
) [% k/ {# t5 @9 E8 v2 t - }- M9 d) i$ S5 u* x
- printf("bind..........
! R7 c; @/ _& H+ T - ");# l p- s" r/ X) w: m! q
-
0 A# w4 d0 ?- J% i/ p - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
6 [/ O) A' k' L g5 \$ s! f - {
. `) S. C! d: U" E4 _' }8 C - perror("listen");! ^+ w# k; X- u. {
- return -1;
# E8 v/ I A8 P& p3 T - }' Y" p' @, |" {( j7 G: O* V
- printf("listen............9 H+ ?' t; m- t: L+ H" m- v+ m6 p, B
- ");- I2 U& ]- ^ `. S0 c( c
- , n, t, T: k1 E" y
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
/ v' l2 {# {1 j' I7 j/ z, Q - if (connfd < 0)
4 m+ ]0 Q3 K" b# ~ - {
5 G) V# _8 d. B! ?- U) y - perror("accept");
: o" [7 I L5 q' U/ u. [ - return -1;
4 c5 }9 V. v. _ G# i - }& B% m$ H: E7 a% n8 ]5 L
- printf("accept..............
% t1 e0 q& S% R# O& r - ");" n/ ~& b* B- P# ~8 a5 R) p* z! i
- char buf[100];//定义一个数组用来存储接收到的数据# w+ k3 n5 r( h
- int ret;
) N9 w" f5 ?9 U8 G - while (1)7 H) Q7 N, b4 f4 n
- {( c' I5 y7 g0 x6 \+ x4 j9 T9 z
- memset(buf, 0, sizeof(buf));& V+ I- u2 @! a! \; l4 @
- ret = read(connfd, buf, sizeof(buf));
( i+ R- ?5 l5 d3 \' n8 f8 g - if (0 > ret)0 z0 Q( O! L H" w7 H
- {7 C- o$ P( S: E" ?
- perror("read");8 O6 P- ~0 b* E$ ?- F2 T) d y
- break;; o+ o) e) y* ?; h) B3 E7 H
- }//执行while循环读取数据,当& `. }5 H' ^; M: I" G. L" Z0 b
- else if (0 == ret)
4 u% d, p W, [, ^ - {) ~" r7 I+ U( S& Q9 ?" b
- printf("write close!
" [! p W$ |/ Z, o/ e }! I; W7 B( h - ");
- n# ^) e( ~7 Z. r) d - break;
) f5 A7 b% u6 W. W5 t - }
9 V9 j) o7 X9 N - printf("recv: ");
: \/ g% {& u& _1 z6 Z - fputs(buf, stdout);//打印接收到的数据
8 ~" Q c2 \% R - }+ V2 c6 e% j4 P* T v# s. y
- close(sockfd);//关闭套接字- B- y s/ L: o1 ^3 H( [) S
- close(connfd);//断开连接- R0 p3 w2 j5 S& N8 ~. Y* h
- return 0;( o5 E. }- I( G' P! }
- }
复制代码 8 O1 a" A* Q1 H n- x# d
, k$ l0 [9 p* }) N# r, ~( e
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
+ V: {) ?/ a) V0 D! ~6 x - #include <stdio.h>
/ o- T( o1 ?, G$ T1 z$ j% | - #include <string.h>: t. a! S* V6 M% X+ G
- #include <stdlib.h>
8 z5 k; y; R0 L) G' q1 q, @ - #include <strings.h>
. t" u* B! u; ]5 ^" b% K - #include <sys/types.h>
, L- t+ W E+ q - #include <sys/socket.h>1 @; W0 X* v5 J: |
- #include <netinet/in.h>
" M5 `' c V. j: R2 E - #include <arpa/inet.h>
7 _% r. g2 h/ \. V8 O3 L8 I" F - int main()9 g% x/ @1 r, |
- {! K, `6 i' n O& D; K' I# h: b% B
- int sockfd;
8 W* z/ L1 t* E2 f8 w+ s& _1 X - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
/ J/ b$ u5 m7 p7 u5 b - { l% h% k5 ^* G1 R8 w0 L
- perror("socket");6 y/ Q: x: v# D/ V; q8 ]6 c
- return -1;" M4 U5 a* Q: x( o+ T* S" G
- }# g$ o5 Y- o# G, E, f# x
- printf("socket............ u" O) C) J; w# s
- ");
$ ^" u- V4 V$ {- s$ k -
" L+ x2 c) c. }8 H$ e' b: M* _ - struct sockaddr_in srv_addr;; `" n- [( f1 D; K8 ~( f1 @
- memset(&srv_addr, 0, sizeof(srv_addr));
5 t; p! l" F/ _& s$ ] - srv_addr.sin_family = AF_INET;# k, v; B/ o$ g8 h
- srv_addr.sin_port = htons(8888);
( c6 ^0 `9 z% L* P/ U - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");1 n9 P. Y) B5 l8 {
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))& o3 ~ i; @' ?; f: c' @
- {
5 _: ^; e% ] P2 _9 ^& y - perror("connect");; ^; {5 ~$ ]4 v1 i& M
- return -1; //exit //pthread_exit
4 d+ N3 ~% h5 Z - }4 W$ |0 I t* v! D
- printf("connect..............
7 i* X; g6 C6 O0 B - ");: k1 L9 }8 C* {7 ?7 M
- char buf[100];
/ Y) w9 j D! E - int ret;
+ G' x, P- r2 v5 Z8 Z& j- U, r$ k - while (1)
+ \5 l/ F/ y7 G% h; R G - {# Z: y0 E8 P2 ?8 f. l) `
- printf("send: ");& `& P3 \% x5 F% w
- fgets(buf, sizeof(buf), stdin);9 S: Z( z0 {5 L4 Z4 e
- ret = write(sockfd, buf, sizeof(buf));
7 F" s; d n5 G" s2 \& H - if (ret < 0)- W8 g) X; c% e! G+ n' \) n
- {
1 h. {" H9 t2 ~ - perror("write");
5 O3 n3 q; M$ A8 x. n9 Y - break;
# Q* |6 B0 D, h9 C7 u. y1 ~ - }; t2 I2 S0 z- h6 L" h
- if (strncmp(buf, "quit", 4) == 0)
' d/ s6 t) Z+ ]5 \ - break;
o* l2 \. m' x" Y - }
' k& o1 `; i; {2 \, ` - close(sockfd);$ w4 N' |0 Q4 H! D/ V: E3 B& F4 P( [/ [
- return 0;
4 W- S9 t/ t2 @ - }
复制代码 $ N0 c$ J2 h m2 f
) n, T; T* R6 I2 `7 R) [' F
|
|