管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
% p& D0 w. x5 N# {- @0 a( Q" u, \% r- M* {1 {1 N. @+ b
' E6 w. m/ O$ R- \3 O5 |
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
4 e8 C# P: f/ [# [+ z2 \: f* a( S$ d* T0 W# u; {. \
4 H, G0 E9 N4 O6 L J0 h( Z2 x6 K2 J8 T- B
TCP协议# n/ C- ^2 [) y2 X, L w" }( N
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。: W8 T& J: L) ~) r9 \+ U3 {
; F# T# w$ W( j9 x, Q
! a& [+ |0 I' A; s关键词:三次握手,可靠,基于字节流。
% B: V7 {* j6 u; O" W/ j! U/ L: b, r5 H$ s/ J: F* F
* L0 P1 f G4 B( y. \" x: k' Y* x可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。- {0 q1 G) V; R; g5 {
" _' Q+ l' g9 q& ~
TCP服务器端和客户端的运行流程/ c6 O9 K/ B) }. I9 G! u9 Y
, g* o% P/ O) m5 m) x2 p( ?% c/ l2 o: v1 ~
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?0 c7 Y1 r6 w& P Z: P7 P6 Z2 z
& h# N$ f! o# z' w& R8 ^
# l/ w$ d [3 _% u1 e5 D" B: C1.创建socket# v" d- [ B* `
socket是一个结构体,被创建在内核中! k: x; g2 I! J4 b
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ E. m1 l8 r+ q/ w4 r" s
7 l. G/ v( j7 D
$ Z4 G3 M7 ^1 O: `2 ?! }" q" \$ `2.调用bind函数
9 s; u u8 J' L 将socket和地址(包括ip、port)绑定。6 _, T: s# k, E% R
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) @* E+ f4 W0 X! e3 N
struct sockaddr_in myaddr; //地址结构体
9 z# K* d! h% u$ B+ i. S bind函数: [0 ~ O/ j: X0 H; u4 n( p
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
. v! [1 V4 I& g& T8 Y/ a2 v# d( n4 f" F/ I
/ f# A8 P8 D1 m# l' |1 Z3.listen监听,将接收到的客户端连接放入队列
" D! E2 e" N/ i2 P% o+ E( W$ @ listen(sockfd,8) //第二个参数是队列长度
0 _/ @' [9 C& {$ X7 `3 j
0 R0 e5 j0 d3 X& s0 l* W" t( n g2 G) o# ]( x& ]/ g3 o
4.调用accept函数,从队列获取请求,返回socket描 述符3 z/ V3 R! ~# O; e. ~/ T, `
如果无请求,将会阻塞,直到获得连接
' {- A: B! h2 d9 @" f9 h int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
' d) Y e' O; |4 m; P
. d9 M% P+ G. A/ V2 z# L* F# E( i% ~9 T+ @. M7 g- A9 D# ^/ D7 w+ R. _
5.调用read/write进行双向通信
& Q' J( _! W' h8 q6 d1 W0 ~" w! M4 R% M$ Z# P- m& Q
1 H0 ?; P# h' Z8 N6.关闭accept返回的socket3 h `4 t( ^& L1 i; s! {6 k' j( {+ q/ X
close(scokfd);7 o8 G5 \" t- R) C7 ]
; C+ g% |! R' @ ]; l+ m7 r/ j, e3 ~% I, S+ L
* L& W; @3 g$ x Z, z, ]! h. k2 t: u/ I* @0 F6 ^" [$ ]
下面放出完整代码
: I7 g; x5 a; A9 }4 o) V6 L) L6 Z
6 b$ @9 i! f. X8 x2 H: r- /*服务器*/0 R# A0 f) V- [9 d1 t
- #include <stdio.h>8 t% W: Z) c5 a9 ^/ l, H
- #include <string.h>
/ E- M3 u) {& ]) P0 w0 Q8 \& x - #include <stdlib.h>) |2 S4 J' B! o7 [/ z4 m
- #include <strings.h>
# u- c: m8 Z% D S - #include <sys/types.h>' h5 r2 s8 |: i- w4 D
- #include <sys/socket.h>7 N" G/ N& ]) L+ Z$ l7 d0 u5 S
- #include <arpa/inet.h>
9 f0 B! {/ Z0 s( @# y1 i; f- t - #include <netinet/in.h>
9 c9 n) D3 {( b& _4 _ - int main()# Q. } ^3 L m6 ]6 O: h
- {
* ?! R! C4 V& f K9 { - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字8 u* W+ s+ X' }6 V( i3 Y
- if (sockfd < 0)1 i3 i2 J* }9 ?$ p7 R
- {) \3 z* c( f1 ?. T
- perror("socket");( q k- q! S' }. ?: Y3 g% U: E
- return -1;6 b1 u8 i1 R" ]1 J1 D, e8 K
- } //创建失败的错误处理
$ S% A9 \2 ?& X. w; j - printf("socket..............# B% y+ Q' z! R; l+ z* I
- "); //成功则打印“socket。。。。”. M- E% |1 ]! q2 W: F% j" X) L1 Y
- : L0 s; Z8 i8 Y. `
- struct sockaddr_in myaddr; //创建“我的地址”结构体
" T1 \4 }0 {2 n: | - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)! u# F7 ]% K. z/ u- L
- myaddr.sin_family = AF_INET; //选择IPV4地址类型 u5 K( b# l0 O
- myaddr.sin_port = htons(8888); //选择端口号
5 l& ] g C$ }7 n# z. c1 m7 Q - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
5 J4 y. V2 z$ L0 \0 x, g3 L8 T' W -
; p' M5 h0 F, Z6 U2 Z$ C8 `% E - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
( c8 h, b" l0 z7 ~& F6 q; R5 h - {
! n6 ^, C! y5 Y: q2 y6 f - perror("bind");$ p' H+ t- k' j+ V, E
- return -1;+ V" h( K' i1 e
- }
6 e2 n z& t7 t0 P+ f, } - printf("bind........../ {6 @0 j- [0 U# Q' z
- ");
% {4 `! |; ?: V& M' I# H' U) T9 e -
# w, B; u7 h A2 w( O: P- R - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听# X1 y( F' h" u4 k- Z5 m
- {
3 Y4 k5 O4 c5 x6 H - perror("listen");
8 N. m2 I. k- D- Z - return -1;6 O7 d2 N: @ `& v: O
- }+ M7 E) M- N& e* g
- printf("listen............
+ W- G6 O2 {" T: g5 V, i. |! w: W6 }' u - ");8 |* V1 f. l; `
-
" [9 w! V' i/ t; b4 E: i( A) ? - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
# V2 u3 U+ S8 I) B* j7 W - if (connfd < 0)6 K/ m }$ e8 B+ ]! w0 {
- {
( h9 O: D5 q, v8 |' n - perror("accept");
6 y4 f4 Z) \ z5 X - return -1;" C2 ? g- `1 ]5 @. `, Y: [- A! i( ]
- }1 h4 V6 w% z* @- X- q
- printf("accept..............5 [% f; Q# Z+ |: Y5 }
- ");
; X* c6 I( E. Z* p# j; ?) g - char buf[100];//定义一个数组用来存储接收到的数据* a0 C) \0 S9 \3 \4 B3 R: U
- int ret;5 m% k' @4 }' q7 x- E/ r6 ^
- while (1)
H6 p6 h& l5 z0 a6 b - {
; F$ `- ~+ E3 W" N5 J - memset(buf, 0, sizeof(buf));
( f& N1 w# O: @8 z - ret = read(connfd, buf, sizeof(buf));
- _" E b' W p - if (0 > ret). @' P- v% c, ?) h. D" [2 Q9 ^
- {9 P' ~, P4 `2 Q5 M0 u% h
- perror("read");/ W+ K: C" c* x% C& {
- break;
% P7 G' A; g& X% M& i. x - }//执行while循环读取数据,当$ J; E, D' } p
- else if (0 == ret)
5 }, r" |, R5 ]" M" j. C6 i - {
" r4 m, Q. _1 U$ ^8 t+ F8 w$ ~% } - printf("write close!& X" N9 w+ s$ D# O' H. q1 B: P
- ");
, B( y% u) E/ J+ w+ I - break;
' ]8 p& j! r* ]% K - }& t+ E l5 A/ ]' d; o. Q( I
- printf("recv: ");8 L3 a8 l7 `9 q) o5 J
- fputs(buf, stdout);//打印接收到的数据
# J7 l- D0 H# N& `' ?7 T* l6 N - }
7 ?5 D3 O) ~/ F - close(sockfd);//关闭套接字
* W! W0 @! R9 }" n6 k* W - close(connfd);//断开连接
, P& k, j/ h. }7 ?7 x - return 0;4 I1 B3 P! p. A7 E9 {: J* F1 M
- }
复制代码
2 w# f3 q) Z+ O$ ^- _3 ]
8 L5 c7 r) A2 D' r N' i1 N- /*客户端*/(具体功能和服务器一样,所以不再加注释)
* g! A' i& ~6 [% M9 T. k - #include <stdio.h>
) h1 |3 X7 ^ ~9 {. ?! o - #include <string.h>3 d" g% O. [0 U" y& p
- #include <stdlib.h>
$ j: Q0 m' `, w7 D: b- T/ @. v - #include <strings.h>
B: _3 S0 l% M. G* o, o* O. R - #include <sys/types.h>
1 l7 H4 D" H+ g! R( @* r! |* ^5 `+ w - #include <sys/socket.h>, f; s1 F! }2 C+ ~+ J) X r
- #include <netinet/in.h>
D* N7 d$ Z5 E - #include <arpa/inet.h>' f8 @; G/ M8 t. Y. Z
- int main()7 C) g: f8 B' ]& E, h+ O4 U
- {" C P5 H! R9 W; g2 c3 Q8 x' p
- int sockfd;
/ f' D& C$ B9 U; S5 q" o8 R - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))$ ^/ q( o* J$ V0 R
- {
! `- ?* @! @: u' o - perror("socket");" M8 \' m. ?. G
- return -1;. t" B8 f9 Z: |& j. I
- }
: v' `7 D& |; P - printf("socket..........., H$ u1 b2 q' K% M+ B4 T4 c1 x
- ");# Q+ E2 x' s, z: D$ |) }+ J, F a! t
-
# X$ ^" @" b5 R* | - struct sockaddr_in srv_addr; |" [( c# ?- d
- memset(&srv_addr, 0, sizeof(srv_addr));
- `! i8 ~& v9 W$ g1 u0 n - srv_addr.sin_family = AF_INET; z, B z0 j* G! E0 @
- srv_addr.sin_port = htons(8888);5 x! X9 A* S) A, D/ i4 J; w
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
+ r8 n3 {" X8 R: Y. D c - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 m: I3 F4 I4 M( J' l: _8 ]
- {% }" W7 o- m- R1 T- u# h3 h, E
- perror("connect");. _6 W' g" y$ }8 r1 p) H( c
- return -1; //exit //pthread_exit
7 j1 S9 I2 Q0 z( Z - }0 t- e/ {% M/ w/ N: Z1 d
- printf("connect..............
# Q! P% l I( P* W6 M' ? - "); @" H+ _( r3 D
- char buf[100];" C5 o4 f. T+ ?5 z L. a* r
- int ret;1 K, Y$ b8 a* Q) w& e4 I
- while (1)
2 C- ]- e# E ~4 r - {& i' U# Z: S- E
- printf("send: ");
4 A; P% z& a4 } - fgets(buf, sizeof(buf), stdin);/ I( y* _- |6 i. R& r4 H( y
- ret = write(sockfd, buf, sizeof(buf));
3 l3 b1 d& L* u* l1 N - if (ret < 0)
+ K5 x- E- H S+ x3 }* G$ ^) I - {+ T; |7 A1 o: |; c/ X
- perror("write");
% e5 j8 |3 t3 M3 S: H9 S$ G - break;
/ |. S: r7 b1 m1 t: P; h. {( x - }
. Y( D0 N2 Q, H# w, K- N - if (strncmp(buf, "quit", 4) == 0)0 ~% u, D6 } {7 `1 O; p1 R
- break;6 j n4 V: y' |+ e6 x# ^
- }
0 @* K( {3 H. K% L6 n+ z8 V: w - close(sockfd);
9 v4 h, h1 [( ~ - return 0;; b: o' ? b, b& T
- }
复制代码 3 I& E8 ?; t1 N4 `
. m$ v6 J1 v- o |
|