管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。, |/ b. v; @1 p U; E& X
$ I6 p7 Z' z3 V7 R& b! G+ A
5 b- U) C1 D. K: b( A! D
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
6 Y. k6 ]0 q) x7 \8 N9 q7 f" v. j( u- p* L2 v1 f* t
4 n: D7 x& A+ Z6 ~% w/ TTCP协议! ^, F# i7 i5 n& l7 n4 i: i8 w
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。$ Y) n9 z+ W9 C
! U3 M( M% X' v- t" ~# X! E4 |) }
( L5 M" d! ^ {
关键词:三次握手,可靠,基于字节流。
! X* |$ l) H. o. y' q7 H" N6 |
9 t/ T( t( p0 `
, N6 V9 j# R3 i) x! m可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
% @' }( ]: V. \
; c! x2 ~' c x, o, O9 VTCP服务器端和客户端的运行流程
+ y g6 ]- E* i
0 D1 T4 ]+ Y, a" d _& N" m) N6 p
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
7 F2 @7 u: b0 c; ?. ]
8 M( r! I! m4 I8 W M. a* m5 W. C8 `: K1 _4 k/ T
1.创建socket
9 u8 ^' N2 ^+ n: V" B7 C" Y socket是一个结构体,被创建在内核中3 d0 e, f) v X' d% `
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
3 M2 z( J1 o/ X
+ b* q" N0 d+ s. B
. i! [4 B( c8 G2.调用bind函数
; D5 ^5 K6 W2 }: Q% M+ g 将socket和地址(包括ip、port)绑定。
) B4 i+ |) s/ J7 u) j# h9 ^ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
; h: M( u% e( F9 ~, L" K3 ~ struct sockaddr_in myaddr; //地址结构体& |: I0 a7 J4 V! Z% x, m Q9 e$ D+ n
bind函数% ~6 h) f9 N( M/ Y6 i: N9 {" c
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
0 ~0 O" I, @, r2 M E# t: i- t2 \$ F. m- A }9 I* x# R+ E
7 k$ L" ~) x+ T8 D" ?* v$ ^3.listen监听,将接收到的客户端连接放入队列4 a6 L* {- Z3 y3 B
listen(sockfd,8) //第二个参数是队列长度% q6 _, f6 i) _' z5 ^
) m7 P& Y( u- s+ n1 |! T3 _4 F
5 Z8 S0 C( {5 b9 `7 F4.调用accept函数,从队列获取请求,返回socket描 述符$ [2 n7 e; T* O
如果无请求,将会阻塞,直到获得连接
* L; E3 Q8 p' M int fd=accept(sockfd, NULL,NULL);//这边采用默认参数" }6 D# p \; d6 J* ]+ I: r
& Z0 ?& i) l' R
; W, `) o8 w6 p& P6 n/ S5 @+ `
5.调用read/write进行双向通信
3 H5 n! u# R) f- F6 J' Z1 q5 [* b4 R8 s9 e. Q0 U
; B4 D4 R) E" N. q* N4 f4 M& g
6.关闭accept返回的socket$ n* s; o! u0 |3 i F# @8 s' l
close(scokfd);
9 [# Q' I! G4 W; e0 k: K5 }: m
) M$ C1 P$ m5 e' {
# M1 Z& Z. i D4 D* k( x' ?* M' q" A# v
& ~9 x; ]6 ^) \+ C+ {% Y v# d! R* b
下面放出完整代码
9 G' J, [8 d+ B5 \% j; @5 y; I: ]4 `) `: x2 _5 e
- /*服务器*/
* S* H( \/ n- ~, c. a - #include <stdio.h>
) n* F/ g5 O$ Z7 w! L2 L$ k - #include <string.h>$ r- e2 v; L2 v; y5 K
- #include <stdlib.h>
# N$ m7 f: e) Q- f& m - #include <strings.h>
! b& _' _, D1 P# F' \. c$ s \ - #include <sys/types.h>
8 E3 d( W0 ? T& ^- L# e - #include <sys/socket.h>/ r) d, U+ i/ |* J$ U8 u% Z
- #include <arpa/inet.h>- t/ z. L; R- R; B* L. i. Q" z
- #include <netinet/in.h>
# w+ d( Y! j& F1 A1 \' R - int main()5 p6 h& f. q. B n+ j% t
- {
5 z1 ^4 v2 u- A( f, v - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字 S& t% T4 Q. ~' j- ]1 f
- if (sockfd < 0)
3 g3 n v1 t' H% g# S+ I - {. A3 m& L; R; \9 @3 s" z
- perror("socket");
0 ]5 o3 v& D! z' ` g. C - return -1;
/ ?7 e! g, R( O' R! y+ M/ j8 j - } //创建失败的错误处理: ^; e# [1 f4 V. I3 [7 ^
- printf("socket..............
& k" b+ m3 T6 ?" [/ a* G - "); //成功则打印“socket。。。。”: ]. {/ O F9 ]) n+ u/ Y/ N
- * l8 V5 B/ A9 X
- struct sockaddr_in myaddr; //创建“我的地址”结构体
2 ?& H* U5 A: q$ e% f: d$ G - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
# Z/ l6 K' W L2 k. t: C - myaddr.sin_family = AF_INET; //选择IPV4地址类型# F6 ~* s4 r/ S
- myaddr.sin_port = htons(8888); //选择端口号5 {4 B: I3 {* Z! L G7 q* {2 Z3 \+ c
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址( X$ @, N6 n- R5 |9 p
-
& Z5 e$ c/ ^, f3 |1 r' m - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
" M9 p T4 g# n - {
0 o+ x1 H7 {% M4 |2 b) g - perror("bind");8 Y3 C _9 T# J6 X7 M h
- return -1;1 C n2 p2 Z# l$ @$ A; p! }& C
- }: p6 i+ r0 D0 R9 B6 w) t
- printf("bind..........9 i! H& i) j1 R( X# o( L6 G
- ");
+ L9 E+ c; v: y -
% _5 [ x& h$ M& k0 J* q. n+ j& {! O - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
3 ~7 f9 _# |1 O) g - {/ n/ N3 W0 I- F% x
- perror("listen");8 \' w* _! G4 z+ a1 R
- return -1;
, \* V+ f' `, g - } j3 u, c' y5 i4 M$ o0 Y( ^ {
- printf("listen............
$ T1 W9 |; O) B" \# m1 y - ");, Q+ S; W; k0 v. H6 l# r7 M1 `
-
. J- M( [' C5 e1 X - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
4 o+ j8 d! J: J" R3 u* G - if (connfd < 0)
3 ]/ V# b$ Z* L8 t1 u4 H, d7 M - {
) l0 |4 E1 P) c/ B# } R, } - perror("accept");# f) a+ z. m# i" Q+ l0 b& K" a0 l
- return -1;
# U g% ^1 s5 D# `1 j2 G - }- m6 S9 i$ n3 W) \
- printf("accept..............+ g0 A7 _' Q/ W5 O: J
- ");- j7 M# [7 z& m6 I; z
- char buf[100];//定义一个数组用来存储接收到的数据
9 {+ y( {% o$ v1 I- W: n - int ret;' K0 n% X3 ]8 m- |1 { ]! n
- while (1)* P) v3 P- k2 p. l6 I W L
- {
' n6 d; M. j8 W& c# o) h - memset(buf, 0, sizeof(buf));" T( w# e) g9 Q$ R
- ret = read(connfd, buf, sizeof(buf));
' T; Y' U) t0 m1 t r y - if (0 > ret)
; D/ D/ K+ h2 n! s. j/ I: l - {0 H' l3 E, ?& N0 B2 Q j' ~
- perror("read");) m. G' O9 b: D1 s# L" q8 {
- break;
* m/ }' n3 [" s p( l: R* N - }//执行while循环读取数据,当! V4 b8 E7 h* x/ Z
- else if (0 == ret): Z3 g0 l2 f6 G; h5 n7 }7 A
- {7 l( W, q' ~- `
- printf("write close!5 {: K% l6 @* d1 g
- ");/ V8 p; L7 M1 _
- break;! a$ ]! E0 `( U& p. N1 P6 m
- }
( F2 }* n( |6 p - printf("recv: ");
1 n S$ L) u" f% U+ _, [& b3 z7 h - fputs(buf, stdout);//打印接收到的数据
6 @* D. y/ J! R4 {4 I9 x; C - }2 k7 I o+ ^7 f! I( v' X. w
- close(sockfd);//关闭套接字' Y* s4 t: \/ U( i+ i
- close(connfd);//断开连接# S- Y( x/ \" `4 |5 e7 ]/ k6 c) Y+ p
- return 0;
# w8 l$ ^ U" d - }
复制代码
3 p0 K- |' Q1 n; X) ~- H
: k" Y8 u- X9 {/ d- /*客户端*/(具体功能和服务器一样,所以不再加注释), I; t) x2 T4 C: N
- #include <stdio.h>- S9 F1 X5 M: T# E# Q. p, P6 i
- #include <string.h>; ~3 W5 y2 T& b' |3 a' E7 |8 I
- #include <stdlib.h>. q7 L) o8 {& B8 Y# R
- #include <strings.h>
- i, d D6 q" K% m9 [ - #include <sys/types.h>! n! z' |& Y5 d+ n- E
- #include <sys/socket.h>; V b/ r( N+ y) R, B! |1 t" o
- #include <netinet/in.h> i; ^5 [; N6 C9 B) ~3 T
- #include <arpa/inet.h>( q6 c: M( ^+ v: G8 J
- int main()
: o* D' _& N) }# d - { ^% P* r3 d, o7 f+ S- C# J: M
- int sockfd;
0 s* G* y& D% S; [4 x - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
, Z9 T; o. Q* B* [" q) C - {
8 z; [, o2 b/ j& X3 r( {8 N6 [" g - perror("socket");
" H4 _+ R! I; P4 f5 C - return -1;4 l$ C& Z) V0 p- g
- }
+ U' F @8 A4 K$ a4 L G% h& h4 W: _ - printf("socket...........( ?4 T+ y0 h4 [' N5 s! m
- ");* n2 S$ [2 W7 F, K
- 8 [9 Q8 K% S, }/ v& b
- struct sockaddr_in srv_addr; l7 b- m- P6 U% P
- memset(&srv_addr, 0, sizeof(srv_addr));
$ J6 B6 ^0 r- B. k/ G7 k& l3 K - srv_addr.sin_family = AF_INET;, L7 Z5 |# V9 ~% v/ P8 q
- srv_addr.sin_port = htons(8888);
4 m( C2 v- E$ { - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
4 Q5 E [: B8 m& I$ a. k - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
/ U3 {4 G6 u+ D+ W- a2 K/ U7 @. c - {
2 b- D* Q! q6 V8 Z+ x3 m. F - perror("connect");
2 t$ t8 D' |0 F3 O - return -1; //exit //pthread_exit
- O' x: x' z8 }, ~3 h - }
' C' M V' p; w& Q. g! Z9 V - printf("connect..............' _* v8 k4 F. t7 V) H4 O9 i
- ");2 |) d ?9 S1 Y4 z* B
- char buf[100];! w: T; w/ d7 n( L
- int ret;
9 G2 o1 g f4 P - while (1)* R _- Y* i+ ^6 T7 h/ T9 }
- {
8 Q# Y# p }# C+ G* y( a - printf("send: ");
7 u2 F# h" U: s# | - fgets(buf, sizeof(buf), stdin);
4 Y0 @2 W, Q4 G! l' [1 h; B q - ret = write(sockfd, buf, sizeof(buf));: z4 |( J; T' `$ i
- if (ret < 0)- L- r" S H. _7 G
- {
) J" q3 f% J. }4 B) D - perror("write");
, Z$ X/ k7 _& M0 f - break;; t4 }; Q: }7 p4 w7 |/ L6 Y
- }
; m* W! A( A- p7 \5 I - if (strncmp(buf, "quit", 4) == 0)
9 j. A; F) D5 C$ y, D1 w# j - break;
F& ?7 y8 q/ I& ~9 V/ }: c - }
- F0 |. D& I9 S0 G3 k - close(sockfd);
4 F) J6 g0 D/ z9 s5 ^9 C. Q8 {2 o5 l - return 0;7 k& r( a) c5 y6 S
- }
复制代码 0 }1 }: u* ^$ u0 Q" n- V
5 f: ~, L- q% r7 @' X
|
|