管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。9 F. k( B* d2 H6 ~" N i0 i
9 F2 u' d( B* A1 }" e& D+ u C; P8 D; ?* {) n7 M7 g
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。/ Q# C2 p- u" }% n7 U2 I
4 [4 h' c& t6 s0 p [! o2 p
1 b: d$ X7 n5 V& H y; }TCP协议
3 W/ t; u# ~3 P2 r! q. i4 Z- lTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
+ z8 V& c* W9 L
+ w/ b; u) z" Q. c, g1 F2 [7 _4 G
5 x) G) E; w, r关键词:三次握手,可靠,基于字节流。
1 b7 F1 B! H3 y- T" w8 y- j& o5 P' z G) d" `" z# e2 f( {. L
% N, A! G. S3 @. ~0 U
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, U- @) r4 o' U+ m8 T
1 |8 n* k1 c- O' Y" H6 k
TCP服务器端和客户端的运行流程' @7 v4 o! d# }7 n. ^" `2 o' B
! P8 a* c/ Q- @1 n$ ~* B' T
7 @. N$ r7 |! V; V+ b3 G- h如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?- H, B% |+ f' |! R, x9 w; f3 j
2 I4 k- T6 T6 b6 Y* O k$ q5 j1 R! Q; P; ^6 e+ j2 ^/ C$ P
1.创建socket
{3 ]3 _. m$ F' | socket是一个结构体,被创建在内核中: p8 [5 u' {2 ~5 q& U* ]
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
+ J6 v6 K) H+ M/ D0 j8 ~) Z N8 F2 [: |; s6 V6 j
1 B+ ` t4 ]/ q2 c$ r! W. N
2.调用bind函数! c. N" d) Q. j* Z: o8 \9 M
将socket和地址(包括ip、port)绑定。- n0 W, m8 ?( W$ M& ]) g
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
* S$ `3 h4 u+ J0 U8 S struct sockaddr_in myaddr; //地址结构体( Z3 D0 R3 T! K0 ?1 I1 H* {
bind函数, j: ]; A: I( _
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 d! J7 F4 s1 ^! ^" Y
/ K( b3 Q/ x V
- Y. j. g% {) H9 _, \8 J3.listen监听,将接收到的客户端连接放入队列
! ~' M2 R# s$ n listen(sockfd,8) //第二个参数是队列长度
* H4 A( k$ x t$ s/ U% d3 J( o, V9 Q( V$ M
+ m$ G/ x) |. w6 [: W; @- p' n p W4.调用accept函数,从队列获取请求,返回socket描 述符6 z+ I2 _& ^5 g' j/ c, \& [
如果无请求,将会阻塞,直到获得连接
4 }! L3 C8 J& ^! I% `1 J3 [ int fd=accept(sockfd, NULL,NULL);//这边采用默认参数 {- C. N5 C) l# T3 H" V; e2 [
" C2 H1 f! j5 k4 K0 S( z+ A9 |) ^& G
5.调用read/write进行双向通信
7 n5 X( A9 \$ Z: m* \& I1 Q6 i; x) ?# F: S4 C/ m4 I
+ {& h0 A/ a# r/ C* W2 H6.关闭accept返回的socket
}5 G+ i' f {$ l) e; ~' W close(scokfd);
* i# D7 s' q2 {$ A. Z4 Q" t1 T" [6 ^6 o s9 L4 h3 G! t& E
7 N8 W9 S2 y% C. s* }7 E b) \" l& m* ?, |9 h8 k
; l: G. @4 a5 n# H0 L; {
下面放出完整代码
0 R: u9 }, Z0 j* T
- r+ C- d( U! C' V5 `8 [- /*服务器*/
6 X: C, X% a$ @( Z - #include <stdio.h>
% r0 z( ?2 _( k6 k8 X* t4 [; I( X - #include <string.h>0 _8 o" U8 e3 P& {7 E
- #include <stdlib.h>9 Z% r3 ]; ~8 u! D+ ^; ?. @
- #include <strings.h>
8 K) |/ j% ]8 y0 f7 u - #include <sys/types.h>3 ?8 \( R S0 X
- #include <sys/socket.h>6 q2 Y# q2 ` p3 p& u6 ]+ e
- #include <arpa/inet.h>
# p. `$ }4 h' s' N* r - #include <netinet/in.h>) W9 \4 \( z: F) g( Y6 N
- int main()% `0 o% y9 s% z( }, X f. |
- {9 Y2 c1 D9 w C* @% s
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字6 h+ R2 K4 j2 L+ V3 i
- if (sockfd < 0)
7 I; |3 w2 s3 T& E/ ~ - {
* }8 p3 \) _) {5 m1 C - perror("socket");+ k. R3 C5 \+ w3 r/ \6 s+ b) `* O
- return -1;( L Q* b; E6 M4 o, H" j
- } //创建失败的错误处理
, v9 M0 A# _7 D: p; [0 ? - printf("socket..............
. j3 ^5 Y+ W/ N3 C& P9 ~0 Q - "); //成功则打印“socket。。。。”/ c% T. ^6 ?$ ? f O
- + u0 V1 \4 V& V
- struct sockaddr_in myaddr; //创建“我的地址”结构体
! u% |6 U" m: g# ] h( ]& h - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): b3 d, J* t& e' D) G
- myaddr.sin_family = AF_INET; //选择IPV4地址类型( ^" ~% e( M B6 U
- myaddr.sin_port = htons(8888); //选择端口号8 Z% v8 Y# q, T: ]' s
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
- L' ~, m$ ~! S9 Q4 H& w - " i: M) U2 k9 ^, A
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
, `5 }0 w9 q; J* U - {
7 E- E3 O/ ~; \4 k3 K1 u8 H - perror("bind");
1 K- @7 J0 i* K - return -1;( H/ O) C, w! B0 @6 Y f2 l3 o; F
- }; q; i6 f" g( n! ^" e4 l3 j
- printf("bind..........
3 X( k& q; i7 M! k1 W, l - ");+ \, G$ s$ T& B) `3 S& t( G% ~
-
9 i1 a6 w# \: n' M7 n' I - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听7 l9 H+ j4 W. O+ A2 F V/ t8 b" U
- {
% x8 \5 V6 u3 I x+ p - perror("listen");$ V4 M6 i# l! x) ?6 f2 \
- return -1;
2 T! N' U2 w4 z" ]& _ - }
) p5 V$ R( [, k. F/ N - printf("listen............
, E) p: b" q. j - ");( V9 O: z- c6 y) z7 P. d
-
1 [" D d% W( n7 M6 t6 g# V - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求! g: e; n! i& d, o* q
- if (connfd < 0)3 D& t" h) ^1 f1 ^! H0 e
- {' {0 _$ d; M2 }5 R" T
- perror("accept");
9 v! A; c; J4 t* D3 z# w - return -1;" g; O* E8 X0 V. G ~5 Z+ i& a. m
- }* Y# d. S' k- m/ o
- printf("accept..............
. M' X) N3 R1 l- H4 q - ");
6 ?; `0 H% u7 b1 l8 f3 X8 c+ Z( k4 i - char buf[100];//定义一个数组用来存储接收到的数据
$ a; p' w, W% j u3 d) @) Z) w - int ret;
3 N8 ?1 M0 F% L: j, |& Q/ u2 \! Q - while (1): P# z: o9 L9 C' Q7 I
- {, z& v* ]5 \! } M' e6 Z
- memset(buf, 0, sizeof(buf));
) S S4 p, Z# J, E! j- Q1 I - ret = read(connfd, buf, sizeof(buf));) I2 A6 J! F; z! y# q, t" [& o
- if (0 > ret)
, y% V! J& @0 R - {
) |7 k* b8 R0 O; p8 `6 r3 ? - perror("read");7 G4 I( H5 g, @0 A7 M0 Y, W
- break;/ W1 @/ m$ ?" w& B3 \ N* W- t& O
- }//执行while循环读取数据,当
- x7 m- U. I3 d) x! C/ n - else if (0 == ret)" X" t9 h$ G4 H$ }& n
- {
) p! O }' L* E. g0 W: O - printf("write close!
% ?. b5 j. G& j2 } - ");" e& Y$ a/ Y% F
- break;
$ F; K" u% J' l$ B* E - }; g0 B3 f7 @2 f( L1 M
- printf("recv: ");0 r7 h7 h' K% w$ M$ n$ d. C: q
- fputs(buf, stdout);//打印接收到的数据
2 _$ Q" x" h4 ~5 n, k# Z - }
/ m; R' ]' U! D) C5 U - close(sockfd);//关闭套接字8 V8 Z4 h& s* {9 J
- close(connfd);//断开连接; K9 }) _' W# x3 f9 {6 @4 S
- return 0;
2 K- z v2 }1 s q- C% {1 f" K - }
复制代码
?' I1 @# Y; x8 C* ?. [8 f8 e2 K1 y6 u5 }. a
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
- W" R( L; G6 @9 e - #include <stdio.h>
8 ?5 a, N4 B# O0 f - #include <string.h>
7 S+ b+ Z! n, ~+ {0 ?, v7 n; B - #include <stdlib.h>
; u9 ?4 Y$ m2 k - #include <strings.h>$ [9 ?5 j2 i, ?$ v% O7 |9 y
- #include <sys/types.h>
! K: u K6 G: N% ?: H - #include <sys/socket.h>
- r" o" u5 x4 k - #include <netinet/in.h>( d! \2 i* l& S/ |
- #include <arpa/inet.h>
! K5 R- H3 C% `5 y+ y - int main()% [+ l b5 N& d7 i3 l3 f& |9 \
- {
9 `/ u4 o4 ?9 P q - int sockfd;& I Y- W; {: T+ m# z
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))6 ]4 i1 u1 c8 U; G" R) F
- {
, ]) M" o | c9 U+ D- Y6 K - perror("socket");, l, y; K* D7 I8 \3 H+ b
- return -1;3 T8 [' R, o8 I& R4 _$ L
- }
! B. r% i5 |" e! Y S- A% q& _ - printf("socket...........4 Z% O( I5 K, r3 h w$ u( {' s" M. e& P
- ");- M- X3 R* N2 I2 ], E3 o1 B
-
. O4 `; s% r& Y - struct sockaddr_in srv_addr;
5 U: ^5 p# G* i - memset(&srv_addr, 0, sizeof(srv_addr));
% K0 m3 j4 A& G$ B c, m- @: r - srv_addr.sin_family = AF_INET;. l& N9 o0 P. W
- srv_addr.sin_port = htons(8888);4 V! f1 n+ k, Y9 P
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");: c' F9 H1 v) L: q+ _
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
7 H2 {# h( }( r0 H - {
( [; z S/ y7 a# n, a; W' c1 { - perror("connect");# \1 ?+ A4 L: f5 V9 X
- return -1; //exit //pthread_exit
+ g' M) H: f# D8 R% z! ^- F% n7 L - }
$ \1 Q$ ?7 H0 S* v! Y' B1 M - printf("connect..............
) B' ?! x% B& u/ C/ ^7 I# { - ");+ o9 }) h' P8 ]# @8 v
- char buf[100];) V# h+ Q+ x7 w, p0 F; R
- int ret;0 B- ~* l; m1 h y7 D" v7 ^
- while (1)
! C C/ ` T @5 V+ l: l - {5 x# G$ C$ S* W% J
- printf("send: ");
" H+ C) Z; U; @) Y6 r - fgets(buf, sizeof(buf), stdin);& y; P8 E2 l- _2 x" [* @; N
- ret = write(sockfd, buf, sizeof(buf));
2 D7 p! C( ]' f+ c% b( l - if (ret < 0), u! Q6 ?$ h7 c7 i* K, S( z% F
- {
- L& o6 _% g* h5 g/ f2 M. ^( l; { - perror("write");7 L8 B: ]) T, V" R% p" N+ @6 U
- break;. c% r0 q' e/ `: F5 \+ s
- }
, ^) I5 r0 y% A+ P$ k: {5 \ - if (strncmp(buf, "quit", 4) == 0)- r/ N9 z2 k" O* D& ~! z5 n# r) i
- break;
1 v! g9 t6 T; {6 q1 d9 X - }& J) |1 j9 i4 B$ M
- close(sockfd);+ h# z( }4 n8 N2 b: p
- return 0;
1 Z- y. @4 O; Q. p - }
复制代码 9 ~( H4 z+ q* w3 }/ p
: d8 l% }% I" ` |
|