管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
; R _* B, B5 Z* o% L$ v V4 f# H7 [2 W. j
" L7 y, p4 ]& X* J5 z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
# _, g) J# i/ C9 F; Z- x/ N
% ^6 q; E: z- G; \, x; x' g2 Q* U5 j7 @4 K) g- O1 u
TCP协议
+ c! s, q6 y$ ]0 |1 W3 NTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- @ E( l3 f/ Y$ i# T* j3 T
- J( A& `' c, K8 {
$ T8 O6 k' K0 D6 ^关键词:三次握手,可靠,基于字节流。3 ^8 Q. s& \3 `/ X
0 {, v N& ]. ]' j
" `$ F5 g! x% w* Y G. x可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
8 A9 Z, V5 Z' r" M# @
- Q' y* l* N7 h! I- Z9 g
TCP服务器端和客户端的运行流程3 Z% w: L0 D) x' y) k
, v% L2 w3 r9 l i: ?$ t
8 l y+ m6 _( z7 m. [1 B5 x9 i9 a. p如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
: x7 T; k7 j" ^1 _. A# ]" L4 a$ f
/ n% T* T) X: v6 h
1.创建socket
0 i- y7 U, e# B socket是一个结构体,被创建在内核中
4 x. d T& q8 \8 p6 ? p sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& o+ }( c9 U" w# y+ s+ X/ s% \! R5 z2 [, S7 n$ |
# c" q' h0 e% ]' a2 m
2.调用bind函数* G" K; ]5 L- V/ j. ~8 j) k" m$ v# X) f+ v
将socket和地址(包括ip、port)绑定。
6 G$ y# s4 T7 o/ G 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
- ]7 _ U6 |6 H" ` struct sockaddr_in myaddr; //地址结构体
5 A, H# ?; p! p" Q& N( ]( ^% [ bind函数
, Z* }! D& }1 D+ y# Z9 n; v! _% m- A5 l bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
8 [0 l+ V0 i+ `7 f& m5 x7 ~ v( e5 n3 }( K. J
3 Q7 n6 i, Z7 \' y# @3.listen监听,将接收到的客户端连接放入队列" l# s* g: }# O; G# H' I
listen(sockfd,8) //第二个参数是队列长度4 M7 Y# B# u f! J0 E" L
2 S, k: M% Q1 L* z
/ l% F# \* r, o8 {3 M+ c! {/ u+ Q" h4.调用accept函数,从队列获取请求,返回socket描 述符
0 O% z' Y; D5 X3 G; P, ` 如果无请求,将会阻塞,直到获得连接) I' J2 ?- F6 t R+ |
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数4 P+ j6 c% C8 D5 N/ Q% `; |
* h# V( H* u- B! ?, R
o9 B; H1 V9 M. K/ w& T7 e8 l1 S8 Z5.调用read/write进行双向通信
" Z! i2 y" H* _3 p ?( D8 t" H! H
5 T9 _3 p6 _/ u" Z/ a3 s" Q6.关闭accept返回的socket* X0 ~4 z' c& q0 H+ ?$ ^, L5 H+ O2 ~
close(scokfd);0 j8 P1 @7 g; A7 I
& h5 e2 \; |$ R B
/ }$ M1 H9 ?# ~3 f) ] E9 `( L+ f/ f/ t$ l2 g8 |9 c
" g- m7 C* T8 Q下面放出完整代码6 ]7 u, Q D, C7 ~# ~
9 f9 I9 R; m% }. a! Q9 t- /*服务器*/
( U& T4 O# u3 s0 _ - #include <stdio.h>' M: [/ l3 c1 u4 V. @
- #include <string.h>
5 O* g' |9 v% n; l3 i5 J5 H3 G - #include <stdlib.h>) l [- p/ r* F5 F, `2 ~7 G- Z
- #include <strings.h>
; g, h; \. k( k% \$ w7 H, ] - #include <sys/types.h>( W3 ]5 Q9 y- T4 N+ y1 W0 D4 S
- #include <sys/socket.h>/ t2 [3 K3 e3 _' _
- #include <arpa/inet.h>
* q4 ]3 a) f. G6 } - #include <netinet/in.h>
- M: P2 I- I7 C, Q% w3 t - int main()/ W* d7 _0 z" G; Z0 D: {- f
- {
5 y" h5 d, \$ J1 m, g( } - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# y4 { [. }- P4 ]
- if (sockfd < 0)# i1 D; ?4 F; ]; `
- {
# G: g4 t0 Y5 W& o* i2 s - perror("socket");
) q$ i6 C! v* Y: W I8 r - return -1;% k2 Z+ b7 p: Q7 w' }7 I# {* G+ w
- } //创建失败的错误处理6 U/ b/ T$ b3 L% t
- printf("socket..............
2 \( X0 ~* |* F' Q - "); //成功则打印“socket。。。。”
- ~7 }1 J `( L' F1 y - ! ^- w, ~( k0 u( n8 r; N
- struct sockaddr_in myaddr; //创建“我的地址”结构体& N: K" s9 |% n/ }& o9 q
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)) ?2 \' e, P' E; e6 L
- myaddr.sin_family = AF_INET; //选择IPV4地址类型% B6 ?. \; C. b' o7 a0 L$ i
- myaddr.sin_port = htons(8888); //选择端口号0 I2 x) k( B2 n3 Z. W: j$ Z
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址4 ^. x7 O/ }9 s
- 7 j* m) L+ J, w8 R( ?) k
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
' q1 \/ G- T' j3 N% h4 s( ` - {
7 C( y5 q" t" C - perror("bind");' g; A8 i# a: V6 s( Z9 \! G( b
- return -1;
1 t2 U1 |$ @) s7 N; s) i - }/ `3 U7 W) ]( \
- printf("bind..........
0 Z1 g) @5 A* A0 \+ y6 V - ");
" M6 U+ H4 t8 y8 U - : F& }' n n7 O4 ~7 |
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听0 o" ^' U0 A+ g2 L' Y
- {6 x: C) P6 U0 s6 J$ I$ h0 T
- perror("listen");, O( l' y% o9 \: P
- return -1;" m* x$ a( \5 `" M0 ]+ T8 i
- }
* B" s% ` _0 A- C - printf("listen............& R0 `% _, u$ B) c7 q9 o) O
- ");8 Z+ }6 h: I6 u# U
-
% b- U, j6 t5 W" U - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
' v2 h z4 S/ V, S' W: X - if (connfd < 0); S' k+ h9 Y# X: |; [ b! C# ?# A
- {/ l( B6 ^5 M# q$ o+ c
- perror("accept");
, O0 V. I5 k" S% Z% r+ h - return -1;
" G. p7 E% {2 E8 u+ C$ z - }
. A$ V. J& N- s3 f' i: m6 X: x) a - printf("accept..............
$ k& c5 c4 X& _/ i9 }* g - ");
- n! x* {! ]6 i* {+ s - char buf[100];//定义一个数组用来存储接收到的数据
6 j. j1 F+ f' T - int ret;! F- C- Z7 ~1 \2 `2 i9 }
- while (1)
3 y" i* X, q6 ]! }/ b0 F6 {* m - {8 o9 D# q9 x& r( j
- memset(buf, 0, sizeof(buf));
* U: K6 E7 G$ g' ?# X& K$ R x8 U - ret = read(connfd, buf, sizeof(buf));! m7 Q8 v q8 s; a% A
- if (0 > ret); G. P, k" r7 b* u7 j" J" h
- {; [) |, p+ c7 h4 o/ A5 r
- perror("read");
! s; Z) c" D+ b3 q - break;
" P1 X) F1 Z. W5 U0 B7 w1 C - }//执行while循环读取数据,当# z% L3 S( y( w: C
- else if (0 == ret)
9 [( l* a9 e4 @" E3 N6 @ - {
4 `1 B- T( c/ H/ Z2 Y - printf("write close!3 X# f+ @0 b) c( d7 Y2 l( P! p- H
- ");
6 ~1 r4 g$ f2 _- Y" {& G w& i - break;
" t: t9 |0 }$ f - }
8 U, B- C) k y) q5 J0 ^2 X+ g - printf("recv: ");
! k6 i6 G$ [/ w# ?4 w1 p - fputs(buf, stdout);//打印接收到的数据
) C! ^7 a2 s- i8 s1 e+ H, S; M - }
( p5 H: I4 T. N7 @5 Y - close(sockfd);//关闭套接字
7 S& ^# \+ i: U - close(connfd);//断开连接. z: @3 T2 U& x$ Z- V2 w
- return 0;
2 d" y w; i: ~. ~) x! E/ H - }
复制代码 ( Y$ k% ]5 H# Z/ v4 G3 a
) K; h$ r! t! W+ s0 d! [4 H7 g4 _2 M4 w3 H- /*客户端*/(具体功能和服务器一样,所以不再加注释)# A' B8 m% _ t6 z8 J7 }! P
- #include <stdio.h>
5 Z$ ]( A! v/ _9 ~7 d2 O - #include <string.h>' y& ^( c) M& b( k9 N% o
- #include <stdlib.h>
3 Q5 k) A- v' N) u- C6 T - #include <strings.h>, o- N! {5 V; ^$ G
- #include <sys/types.h>
* y7 o& n" o7 I& z/ J - #include <sys/socket.h>7 t% s" D& f2 s& }8 z
- #include <netinet/in.h>* D/ i. k' P0 D8 k9 I) T2 B, q/ r. q
- #include <arpa/inet.h>
# y$ W) f1 `# I" a' B9 n* o" l! d - int main()8 z. e; @5 z# W e0 d* z
- {
7 q9 @6 b4 E3 X - int sockfd; i& l, T: ^7 }) t. `: f6 x
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
" M. R( \( e, k. r( t9 D - {
0 @& \6 B3 k2 i, I. r1 l9 L - perror("socket");) \4 G8 A6 a& F: H) D8 u
- return -1;
. W4 c0 H; M9 h8 [8 w0 D o% Q' _4 u - }
3 E3 ]1 o$ X1 \ Y( ^! R - printf("socket..........." x# G5 [' g4 M
- ");) Q" L4 H9 T" Q
- : R8 r; F+ Q0 k j: x9 N6 D, }
- struct sockaddr_in srv_addr;% Q8 M6 n7 H2 j* g M& Y% G8 W
- memset(&srv_addr, 0, sizeof(srv_addr));
9 y! ?% M+ O9 [% l k - srv_addr.sin_family = AF_INET;
' s# J% K0 N2 X5 H* K4 B - srv_addr.sin_port = htons(8888);/ e* y; g0 m2 Q2 q
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");5 J$ S: v* Y2 }0 j
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
0 m& ?0 r { n7 f: i* C - {
+ ]2 t. _" B. ]/ ]0 p0 t2 {: e5 v - perror("connect");( ?. p$ U+ e4 R% @6 \( y# {
- return -1; //exit //pthread_exit
* b& s: M. i* i1 U - }
1 d! \6 }# h2 E$ Y - printf("connect..............
! G$ Z, G( U2 I, s0 V/ m - ");4 T( h: k/ p2 [
- char buf[100];
. N3 |: c: r+ d- F, X2 Y - int ret;
7 z+ F9 L7 Z/ ^& o0 m - while (1)+ q u7 G4 B/ f$ a% {6 y
- {& M. K- Y9 W: |; h' R
- printf("send: ");+ X* C c6 J* H: r" A" }: |
- fgets(buf, sizeof(buf), stdin);* e1 T0 w! K9 v( ?1 R
- ret = write(sockfd, buf, sizeof(buf));
/ O1 j5 f9 P# b. }) H( ~3 E - if (ret < 0)
5 G' p+ [$ \# K - {
3 y" D* S1 ]( D1 O - perror("write");' D( N, l% E& O5 {# J
- break;/ F: S6 A5 R4 N0 C" `
- }& o$ V9 b1 C. `/ o) A
- if (strncmp(buf, "quit", 4) == 0)0 J1 ~; ^% t! i! c2 ~9 w
- break;
9 ~( k$ I3 U8 C) U) V - }! s: s) ^2 z) q5 F' E @8 `: J6 |3 \
- close(sockfd);
% o* z7 e& b$ u" d - return 0;1 J g* x# n% |# G+ o" y: w, q
- }
复制代码
2 G$ W3 M+ }9 A
: _2 E2 d z& z4 ]) l7 ] |
|