管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
0 H! |% w7 V3 P; K; c) n1 o& H
0 x$ @8 |: j7 d, @# T4 l9 K' }: I: \ e( b; a5 _- L1 u+ y! O9 x2 z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。1 j2 K9 o5 A, G0 H- q8 p5 I
; b U! t# Y5 U0 ^6 v& ^
9 `( T- H, A5 B+ ^3 f2 {
TCP协议" B2 L( V0 O; w7 S1 O# P1 Y
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
& [2 ~- G' H0 b$ g7 n0 J- K% V" T- c
7 E* s$ Z3 b, i \4 T# a0 B关键词:三次握手,可靠,基于字节流。7 C9 e; i" W6 o* N# f7 D6 i2 I
0 e7 N1 o8 [/ ~/ P
, p; q4 t2 {4 C: n6 e: u: d: U可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。9 b) J# P+ @4 _% V. {0 i7 [
' x5 A% l' U+ o* F, I
TCP服务器端和客户端的运行流程
6 A b, {1 N% p- b/ ~7 M5 T4 E
5 V, Y& ]4 J* `
% W: v* Z4 F, m" w3 }: V* k如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
0 T, Y6 C3 O1 h) \9 l; O. ^
M; j! I9 _' I8 @+ ^
5 t7 d) I+ b% }6 G! f/ ^1.创建socket+ f) Q5 f. u. w- P9 H5 G
socket是一个结构体,被创建在内核中
5 F& P: v2 K* ` sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 }, w9 v0 d. f5 i1 ~# ^7 s) g$ J R1 q0 @2 D+ d( \7 r- |
3 N/ Y9 B: o; g9 N1 i' K6 E* c2.调用bind函数5 M) e# G; y4 R# f5 K
将socket和地址(包括ip、port)绑定。
1 X3 A* F; V$ U 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
" r' x) Q% u0 }8 J struct sockaddr_in myaddr; //地址结构体
8 m( t/ i7 A' J# U bind函数
, Q+ `2 ]- \2 z- f( g4 {& ^ bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))) e, M4 J8 M9 F; ]% x. ?5 C5 R
0 O4 o3 [+ K( C, X7 B `& g$ `) L, o' H
! t. {$ ^% q6 X6 J" A) p5 ~
3.listen监听,将接收到的客户端连接放入队列
. W: a% P) {( S" C1 \5 W' m( o+ |' P listen(sockfd,8) //第二个参数是队列长度5 V& G8 w7 Q' b, V1 Y4 m
& a3 m9 a2 w) A1 S# K+ X7 f1 J0 y# \# N k2 @& K
4.调用accept函数,从队列获取请求,返回socket描 述符7 F+ l5 H# h$ W4 o
如果无请求,将会阻塞,直到获得连接
6 v% s& `; r$ @" ^+ J int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 Q8 Y; `3 P- p" U0 V6 I
6 [" c" ^7 m% S5 P' z& q3 b% H4 N$ D- M% C0 s1 ?- O5 X
5.调用read/write进行双向通信# e- A- r+ d: s% n4 \ [4 x, S
9 u' [$ @8 U# z! C1 R5 }6 l+ w4 e% k
6.关闭accept返回的socket
$ r( s8 ?- G. @( y% f1 q5 O close(scokfd);" w' |( s$ J2 a& W# D
8 C. y& `0 f/ Z
' G0 u% S, o5 J! a9 F
1 q' [3 q9 ]% w$ i8 Q) L
% G% W" D; n1 D' h: H下面放出完整代码
/ R6 N6 e6 a3 U ~" j, ~* z2 Q6 k$ c& H, e
- /*服务器*/
; b' N g) \9 T* m- y; b - #include <stdio.h>
. R) g) |6 s4 ^- A - #include <string.h>
# G; X4 W& u( W& R$ b6 H# j - #include <stdlib.h>
4 o2 ~: z4 u; g - #include <strings.h>
; _& B& D9 r- |6 T - #include <sys/types.h>
2 L9 I+ ^ }* ` - #include <sys/socket.h>! P2 x* l; Y; a- N
- #include <arpa/inet.h>
2 I) T/ k' s+ ]' u - #include <netinet/in.h>
2 I; Q$ Q5 D, V* R3 p - int main()0 x$ Q$ o6 v7 _7 T1 I
- {
3 ~2 z2 o J* C! W% f0 {' D - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
! G4 h! f1 y$ y2 j0 X - if (sockfd < 0)
/ w9 P- ~) j' R - {
" l; O3 {; {% E# r4 e4 r& `. G: e* y - perror("socket");
) U m' ~/ Y/ }- A - return -1;0 K! x/ {0 r& ?0 m' f, B
- } //创建失败的错误处理
+ _8 G( t0 Q, R - printf("socket..............
( K6 H% K# b* x0 M9 \# |3 S* ^* S5 k - "); //成功则打印“socket。。。。”- }1 q; U* d1 c# Q
- 8 [. k- }7 j, Z3 d" q& U. H
- struct sockaddr_in myaddr; //创建“我的地址”结构体0 G$ J0 O6 \/ \ @8 P: X7 A
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见), j3 n0 ^0 O( @
- myaddr.sin_family = AF_INET; //选择IPV4地址类型% f* e+ \+ a, n' |( j. z8 D) ~) y
- myaddr.sin_port = htons(8888); //选择端口号' m( ^' g! n4 }* |# ~ }! x! f
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址0 i% v' h$ |/ d c. n9 c# G
- % V, P$ v9 ]' Q# u4 _; r. ?
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, Y0 C' O. S" z. i
- {; p; o* G6 c0 M2 h. t5 j/ V
- perror("bind");
& E, x( K+ z: ~7 u: I, @3 j - return -1;0 \+ m+ `' ^5 H6 A* M! M$ h' z5 \
- }
1 j3 n; H7 a+ y, R$ L3 m - printf("bind.......... R( q$ N( U" K5 e* M
- ");* m. h1 }4 m9 k4 E
-
& L# W. v! x) f - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
4 D/ l; O' ^* v$ \' Y, [2 D& f$ V6 p - {
% q$ y- |# J& V. a - perror("listen");/ m. s8 j" p0 V" D$ q7 Z
- return -1;- y9 C: ?0 Y5 g7 X3 `, Y; X! Z
- }
D: r8 a! A# d - printf("listen............
4 ~& s# ]: `' t: ^, j# K3 s9 C - ");7 u& ]5 O( m$ n( E# O% B- a
-
$ B6 R5 V N/ ]$ Y8 k7 Y# Y$ f7 _' ? - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ R' o+ G- K0 V* ], z! H3 g
- if (connfd < 0)
' q Y3 Z! Z: D - {$ X6 r" ~+ F; O \; ]1 f
- perror("accept");
) x3 l; Z' q& Q5 d" Q h* w" S) K, K - return -1;' G3 E% V4 T/ p* {
- }3 o" C X6 {* p
- printf("accept............../ I) C. }; G' `$ g9 b/ }
- ");# O J! G E, P
- char buf[100];//定义一个数组用来存储接收到的数据# T# {4 t2 {( s7 _2 L
- int ret;
4 p2 z" P: \; b* J; f s - while (1)* Q/ k5 `' \0 [! R/ u- z
- {
4 I5 `# H0 j3 r2 S - memset(buf, 0, sizeof(buf));
( f& S6 C5 K O) a$ d _ - ret = read(connfd, buf, sizeof(buf));2 K* I/ r$ L% E M, `
- if (0 > ret)
8 F0 }7 X6 ~. I1 O( v" F - {' c+ G3 W2 P% o
- perror("read");9 K) y- F1 ~; [1 K9 w
- break;% k$ I: L& q" C
- }//执行while循环读取数据,当& o7 z/ T+ W Q0 V) d5 ~/ r2 c
- else if (0 == ret)# e! L( w" w& x- r# ]
- {1 t, O7 e; l' F' } q8 R; F# c
- printf("write close!
: I! Z0 j6 ?. D0 ~$ u1 H) K: L6 j+ k - ");) j" ]2 N( d% Y' K
- break;
% L" G, N1 ^- v& n# g9 R1 ~. e - }! x6 x* J% s! h( \. N# A6 @
- printf("recv: ");
8 v( b% U, k3 T$ y - fputs(buf, stdout);//打印接收到的数据
, X. s |- J* j' m - }" ?/ Q3 K6 i3 `3 c8 t) t
- close(sockfd);//关闭套接字
( O d/ y6 a& y. K! U) f+ @ - close(connfd);//断开连接
' O# M8 G- q5 r/ S9 ~4 x - return 0;- @+ _* P$ S0 c4 U" k5 L
- }
复制代码
. n7 N2 ]+ u0 x; Y$ N
1 H _* P) F/ u& V- /*客户端*/(具体功能和服务器一样,所以不再加注释)1 p0 H6 c% Q. Q2 s6 q" |6 t* |
- #include <stdio.h>( @0 |% x* j0 F6 }
- #include <string.h>5 q |; v5 }( e" _
- #include <stdlib.h>
2 q2 @ v' D: {; `9 \# B0 e2 w - #include <strings.h>% b5 B" S% Y# U0 [/ f2 C
- #include <sys/types.h>* h0 D8 Z+ b& L( W
- #include <sys/socket.h>
6 P: f- L: u7 z% C5 H4 O' J - #include <netinet/in.h>
" G1 o+ a; M# k$ x - #include <arpa/inet.h>
. |. t' a7 U) h - int main()! R* U K- f8 F: u: b! r
- {
+ a3 J _6 h* v* V' ]* q - int sockfd;+ a& Q8 \8 Q$ s
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
" R8 m0 F1 O4 p H; S8 y - {
/ K) ^4 i5 M* {- l( m- ] - perror("socket");) s) T. q1 X) n" Q7 q5 S* ]8 ?$ u
- return -1;) C6 B% F% s- ]' z2 S, H
- }0 k& r0 H. I" c k7 S$ o* Z
- printf("socket..........." N6 n9 i$ X3 f d6 \, w
- ");
1 d5 ^7 g( @) A4 I - 4 a1 |1 B% \( R# ~ c
- struct sockaddr_in srv_addr;
- K+ a& E. m$ |# ^ - memset(&srv_addr, 0, sizeof(srv_addr));5 d, b; ]0 x; }
- srv_addr.sin_family = AF_INET;
& `% l& W3 y6 b/ p6 H% l - srv_addr.sin_port = htons(8888);
I$ S, M% K" i1 c - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");( W8 v* B) M6 Q5 w
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))): q) W0 W$ f% c6 Z6 ]2 l3 b
- {
( o {" ]# b# {3 _( R - perror("connect");
, G( i) S1 T/ `+ ? - return -1; //exit //pthread_exit
7 Q8 \) t; y0 T! N% T3 m - }2 b" [& ~- k$ R5 H- O% d
- printf("connect..............
2 S2 f( h' I6 [( l1 ^. y+ @- x# j - ");
' V+ ~4 V+ R( u+ R6 s _& F+ k- \ - char buf[100];
! r+ N0 Z- a/ [* R1 {+ H - int ret;
0 A3 ?' \4 F; t& S/ a% e6 M* a9 ?% P - while (1)% o* \$ r9 O6 M q1 N7 c
- {7 f$ [$ z# L5 I- \
- printf("send: ");
* K) O+ j2 f/ M' N, ] - fgets(buf, sizeof(buf), stdin);1 D( n3 |2 \6 L, L* S; A' U
- ret = write(sockfd, buf, sizeof(buf));
& N- Z6 Y! @. J: U - if (ret < 0)1 Z- n/ b$ x+ M8 S
- {
+ P" K) T- a) E* l! h# T - perror("write");
5 t: r2 ?9 r+ s$ v/ ]' n, D* ]: X7 L9 f - break;# Y2 e% d2 W+ [' a( t9 L, h! \* B
- }" h0 f; o {2 E; @& L" ]
- if (strncmp(buf, "quit", 4) == 0)- q7 Z5 C1 J/ v; K8 Y7 C
- break;# j& O! ^ N# H! H5 j$ a m
- }: A) k4 D) X+ H. U
- close(sockfd);& _$ O6 T8 v0 K3 m; \# P" i2 `
- return 0;
0 \( I3 F, E0 y - }
复制代码
2 P6 l4 f' d! a. i3 B" e
0 M/ W2 k, E" v2 Q |
|