管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
& k- p7 ?3 J$ @. v8 x
, W. K1 L* K1 Z7 Z4 H( [/ H2 I% d3 }7 J1 w. |; S6 z2 I
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。9 {, ]3 H9 h3 h, }" y( \) X
6 d/ z) h8 A7 [8 U! i
# k. ?* K- C; d+ W" CTCP协议$ {* C3 P+ X5 P: E" i' H2 G+ t# {
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
$ C _# k, b3 m& ^9 u: j: s: ~( Y' V. o: ]% Y, d/ V3 b- z) {: i: p
5 j" R8 z# L+ m" c
关键词:三次握手,可靠,基于字节流。
, g0 @4 S! F- m
5 j' t" \' {, x
* R. L, k- @; |# E5 f" w% e0 ^可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。/ ~# Q1 {( [; W( D$ D+ t
: F; d+ t, \& }, D( k) STCP服务器端和客户端的运行流程2 W3 Z- a& p! t" z0 W
0 j+ S5 `% |% I2 `! r1 a2 v; c4 z
3 T6 ~1 t; H2 e如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?; R: b. m$ w/ |1 t6 z" c# ]' s! z
! k) j3 l l9 ]" G! A2 }9 H2 `/ L U8 l! z" M
1.创建socket. ^: f% B7 b" N3 I1 D
socket是一个结构体,被创建在内核中+ Q; a: Z& q2 n' F
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& {5 y; P: M8 K2 A$ _) \+ \2 Q0 L; N2 v
6 ?- Z4 J: V# A: z w
2.调用bind函数# H2 W3 x' \' b6 D7 {
将socket和地址(包括ip、port)绑定。
- C0 D# a! O; g2 v! T 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序7 V# e* Z4 h. {- F
struct sockaddr_in myaddr; //地址结构体0 `7 R% }8 z0 u/ k& g) Y* m
bind函数3 E& q9 d" g- V& }
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
/ O- V. n: r3 ?/ b. o. E
2 i8 }) p2 k" Z7 z! E3 D+ t- K& A3 i- v0 f' ~8 U& B3 T* O) l
3.listen监听,将接收到的客户端连接放入队列/ s% a* C9 P; i3 C& M; Z* n
listen(sockfd,8) //第二个参数是队列长度3 F; V2 v" x" Y( s$ S& G9 t9 ^' A
( v/ x: p; |. Q9 P- X X
, W3 n7 d3 ^# ?" D
4.调用accept函数,从队列获取请求,返回socket描 述符 B: t: _& f9 u, ~) x# q. m
如果无请求,将会阻塞,直到获得连接
8 U5 v A8 ^. ]; g; w int fd=accept(sockfd, NULL,NULL);//这边采用默认参数, M, W6 B) B8 u" F" [9 k/ R
- t5 \3 G' A3 S5 M/ O, }( J( G( K
+ Y+ n4 ^2 `; n( ?# H4 v$ |5.调用read/write进行双向通信7 y; X- U R7 V( {5 P
; @& f! w+ G0 f& T0 t
) _& M" O X2 T& D* k: s& W6.关闭accept返回的socket# `& ~% {9 }% f$ g
close(scokfd);
8 b) v, e/ E; i# F F3 g# u2 Z8 |% r/ w
( q" m0 t: ~) {( [* }7 ^ K( Z3 p$ {( Q; }
& ~6 n5 t7 X) }+ Q% e: }, i; Z6 u/ V2 W9 M1 n- l8 l- H
下面放出完整代码
8 s$ R& f7 b$ s* g0 V/ Z, z; x G' W# L
# O/ F2 c8 v. S+ O7 K4 D. N/ y- /*服务器*/
& F y& [# I* X; V4 s% b - #include <stdio.h>
7 i/ w! c# o @' G4 |+ p" g, ]- d6 g - #include <string.h>
8 O$ O& [ g9 P5 R - #include <stdlib.h>
! @: a- A: W. c+ S0 X+ e5 e7 o - #include <strings.h>
% W5 R9 i# Z& n$ ` - #include <sys/types.h>
) v( J$ r; e! y/ o - #include <sys/socket.h>" H9 s# |8 I4 T1 i( c
- #include <arpa/inet.h>
9 v$ }0 I/ F5 k) L! W$ c9 e - #include <netinet/in.h>8 v; i1 Q* o2 w
- int main()
5 @4 y& C5 {7 f - {% y+ Q' o8 F6 `' ~6 I
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 K" A7 Z0 } c3 w, B; y& s
- if (sockfd < 0)
" f6 n5 M- m8 s$ Z( ]# n) O3 o+ T - {
, R# t2 U0 b. ]" J, b - perror("socket");
# h1 y* k, o$ ?% N- `' X - return -1;
) Z4 X0 Q2 A7 k - } //创建失败的错误处理
# U( n7 ~9 q) |$ ]2 } - printf("socket..............
2 q6 R! s5 T0 V8 g- x - "); //成功则打印“socket。。。。”$ ^, S2 E4 q, j$ l2 C7 @+ a7 l1 {
- 3 V; m! i F$ n8 Q6 \4 L0 C
- struct sockaddr_in myaddr; //创建“我的地址”结构体- o$ u, @- ~3 t% @' `
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)! x* Z1 M* x M8 `% a
- myaddr.sin_family = AF_INET; //选择IPV4地址类型+ R. X& e+ U' J# R$ N' d b
- myaddr.sin_port = htons(8888); //选择端口号
* c. ~/ K/ K/ X4 O - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
' D. b9 s; p/ `- Q" [# m8 X+ q" U - ) R7 y: _2 T3 _- m8 i
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
3 Y$ d& _: j3 {# C: e/ z6 g( U - {
9 a! v' O+ k# t( d - perror("bind");
$ w8 n' {9 T& u0 _! T - return -1;
a! i% f; L8 ^. @, o - }; m% f0 x' q7 v5 g9 l2 ~% a
- printf("bind..........
# ^* F% l6 y5 W( M$ _5 ~ - ");$ v' |8 ^! i1 }5 m2 L# N
- 0 ^4 I9 d6 h; w( T7 P8 m
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听8 i6 T# p1 l& v7 F- g; B; t' ~& N
- {
" O' o+ g0 ], I! g - perror("listen");
" u% s) X* \9 K9 ], t5 P/ K - return -1;
. A" l/ X; |9 g4 ]2 X( N - }" a: v3 M8 I$ z/ S) Y9 w
- printf("listen............
& c+ n8 C# z, [7 ^/ T - ");
+ }$ ^" D' H' C- |8 z$ ?" @) d -
- e' ]6 ]. c: S1 m- a6 N - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求) E: y z( c" t, e A1 E
- if (connfd < 0)
* ~* \8 ^# z/ @ - {
# T) R7 z) {( x$ u7 |2 r3 t - perror("accept");
! g- I3 g' U2 {9 g9 f1 J - return -1;
. k0 y4 g) o, I - }
# t" R0 ?7 w8 }0 p - printf("accept..............( Q) R% K; o6 K$ f! ~
- ");
. O2 y# K: T: j - char buf[100];//定义一个数组用来存储接收到的数据
) [. a" `5 n2 S0 z6 n( x - int ret;
5 y+ e8 ~% _5 S" y - while (1)$ w# g8 R( R& B% k+ `0 q
- {4 \1 [2 U( O u5 S
- memset(buf, 0, sizeof(buf));
3 c- p! b7 W' d/ E- ?$ t5 @ - ret = read(connfd, buf, sizeof(buf));
1 [: u) \$ V n# I" D - if (0 > ret)
+ V' H( O$ f' C1 x7 M - {
9 R9 E% r7 C! O5 f8 e Q, D - perror("read");
% K, C; `# N. a1 @5 e/ w - break;
3 }1 |# b8 X4 X - }//执行while循环读取数据,当
5 E( ?6 j& ?$ Y. H6 F: E) @, v - else if (0 == ret)
- D; J E! u4 d! g2 f: B0 k; n - {' S7 B, W% r$ ]
- printf("write close!! A9 L# t2 ^- o) `) ?
- ");* E: t3 g! y. m1 A+ R
- break;$ X1 b; L- v2 w0 z0 D
- } a2 v2 j U& ?. U* d
- printf("recv: ");
+ `) {) ^9 D' V0 l4 \# X9 v# [ - fputs(buf, stdout);//打印接收到的数据9 X$ F2 N; \& m; t( z9 O
- }+ U6 r% [" d" Z
- close(sockfd);//关闭套接字
% f& J7 V$ S, j+ G8 X - close(connfd);//断开连接1 l* b4 |+ p; P0 `6 Q, v- g
- return 0;# B X' ~; Q/ R9 w; E' l+ L
- }
复制代码 . U" E, b, u+ [+ \" T% t" T
8 n5 v$ p) I1 @% ~- /*客户端*/(具体功能和服务器一样,所以不再加注释)
- f9 Z3 `; e5 k1 f. O - #include <stdio.h>
! V" F" K0 n* _( L& J - #include <string.h>+ S, y9 M+ d/ U1 ^. F" I0 m/ p5 |
- #include <stdlib.h>
: s/ _' Z8 T) E - #include <strings.h>
- D& N- ?8 @. M& m0 z - #include <sys/types.h>! D) ]6 F. ]# `/ W# x
- #include <sys/socket.h>& Q- N5 Y. Y4 z2 _% ]3 c2 l& m; l
- #include <netinet/in.h>5 j: s. s% Y" [, C. R
- #include <arpa/inet.h>
8 F* J: V1 p6 B/ ^, P - int main()0 @* v/ S- Y, [. w3 V5 Q) m
- {. y3 ?: K5 |! r& \
- int sockfd;7 v# D8 m- t) g# K: G2 q- S& c- f
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))0 Z) ^- x* N% k/ V
- {
5 r1 B) D4 p, s: A2 s - perror("socket");& p! }8 D) X: H: k) S! T( p G8 ]
- return -1;
7 y+ o; z/ m1 U0 H* `! `& R - }- F, L8 d2 S- d/ O: G- ^. Z
- printf("socket...........7 ]9 i! G E, n3 k" e7 U
- ");
3 ?5 h7 `6 f: e - 9 @* C2 T# ?1 z& ~
- struct sockaddr_in srv_addr;7 W7 O( a4 I# c4 J" S2 d; V: `: s
- memset(&srv_addr, 0, sizeof(srv_addr));
0 k7 }4 s; f' Q% [/ ` - srv_addr.sin_family = AF_INET;
! \% R0 N- \* X0 n - srv_addr.sin_port = htons(8888);
$ u% G4 e' t" T& W( ^: \3 d6 c - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");4 T( `6 T8 e: P q, I0 R- X% I
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
7 n7 Z$ E3 w3 t) I6 U - {0 f. E" K1 l3 Z/ \# h
- perror("connect");7 S( |+ v3 V6 N: }, j, Y! ^% i
- return -1; //exit //pthread_exit
9 b& Y) ^- c, \7 `* Q. L7 \ - }& I- a8 a- }" w# `; N6 P
- printf("connect..............
) A7 A8 _ U/ E$ F - ");* D8 i3 G! F7 \& h9 l
- char buf[100];! a# \: w6 R8 z9 Y& V
- int ret;
^* y0 U; _" F% g - while (1)& E P& ~- ~: ?7 V, B: ^$ s. F
- {' l1 @# H* T; f7 e2 }
- printf("send: ");
! R2 v0 W# s, j% t# V5 c - fgets(buf, sizeof(buf), stdin);7 r4 n! H1 `3 \( G6 W
- ret = write(sockfd, buf, sizeof(buf));
3 [( U) S- w1 ]+ q) }+ v+ u1 r - if (ret < 0)
- M5 R% A& }/ y+ r! j - {
) a9 b4 ~7 ]" [; i. Z - perror("write");* z# e# j" d: T, L' {: J" q
- break;
& }' Q+ j! l) R% D6 O3 Z: f2 r9 o% ` - } w( y5 h* x4 `% R. l
- if (strncmp(buf, "quit", 4) == 0)% A" a6 ]/ g: j, z8 h5 w
- break;0 }' F4 {6 ?/ L3 ~) I) i
- }
^! D/ I9 C& f7 Q. l% S - close(sockfd);$ w% p- }7 H( U r8 r5 `; }* B
- return 0;% `/ q0 k( }3 z$ X
- }
复制代码 5 t0 _$ p8 _6 w+ C `) r) V- Y% T
- u4 v( d% o5 Z6 Y |
|