管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。( Q7 ^( a7 f( F# ? q( y* ~
2 L5 K4 m% s7 @& B9 F
5 u( d B' H8 T% [7 Y+ ^/ |6 |
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
) H8 ]5 e. J7 f/ o; b( W& Y6 B- j9 z& P. F5 y7 e/ x/ E/ c
! T; ?+ k( u7 X2 D, f8 s6 g
TCP协议' R5 C2 X! b- v( v- w
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
% S1 ]6 M0 r1 O5 p& a5 ^! l! X4 Y4 f' z/ t
4 ~% j1 z- C. i7 J2 Y关键词:三次握手,可靠,基于字节流。0 [ J. `9 L3 F0 t. L2 A
0 P5 a3 @; j& N+ i
) z! C2 [8 `+ Q7 ?6 Z- s
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, j5 e% C, `3 Q }
% ~, W+ k }/ I6 A7 ITCP服务器端和客户端的运行流程/ ?+ H( q2 @) q* W
- t3 e" v9 C+ z& N! ~0 d
2 w& y- w# v. F! |2 C1 L( G
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 b3 v: [3 F V" _( T. B" q' Q( z# n" Y: M7 X
4 O: m: l4 a! l) B4 B* n% o1.创建socket- H7 d8 p" j5 R: z3 b. a
socket是一个结构体,被创建在内核中
! A, C4 }3 \" L/ ^7 E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
/ G3 @' G6 `# R E" J1 I& w( V! o
1 D: r6 l0 w j! v( E" E9 x2.调用bind函数
4 F% k' s4 g' ^ C. P& z 将socket和地址(包括ip、port)绑定。0 @8 o; G2 S6 K
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序! ~5 z5 k7 [; S# ^
struct sockaddr_in myaddr; //地址结构体
+ t: ]8 z6 w' ~. G' G3 w, l4 L% m' O bind函数8 H$ M7 h0 x5 U; S7 @5 i
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 B, C0 l J* L
, n; [0 l% ~. |
* |' ?. o) \! x! x- K4 P0 H
3.listen监听,将接收到的客户端连接放入队列
7 A0 C' B' m! e0 I& v, D$ g listen(sockfd,8) //第二个参数是队列长度6 R# k8 p; W6 ?/ d
, F! R2 M1 p7 a8 Q8 J2 I
, ~7 | R4 {9 n- y- {4 ]1 N4.调用accept函数,从队列获取请求,返回socket描 述符
4 d' H# T& t6 W: I% N' y. | 如果无请求,将会阻塞,直到获得连接
+ b4 B% P, u2 V* G; S) h% N' k int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
( a: B% D4 o9 T B7 a1 k: Q V( o1 @
7 C( K: J3 z) u" ^- O8 _
5.调用read/write进行双向通信
$ l' _1 q. M- X3 J" P' E: i: Y8 ^& E8 M5 c' o
- i* y* u( y# }3 ?
6.关闭accept返回的socket
' g/ G( \. t4 R+ e close(scokfd);
8 ~. O4 g- {9 M' q$ M9 [1 {7 {- _) t0 K" e/ Z5 N0 Q4 J/ D
$ p. U; r$ G: P( R8 R9 W3 V! u- B2 t' F6 d/ p
, x- Z# s; t% Q v下面放出完整代码" n+ \" d7 L% |1 A/ e0 V
; }6 y# W' `+ m$ M+ w; U/ c3 T
- /*服务器*/
* L/ m* K, x, F' Y, o - #include <stdio.h>
& o+ h3 \9 O, F- T - #include <string.h>
5 w: Y6 d# x7 j2 ]" C - #include <stdlib.h>7 T6 a- O: g a& D; v
- #include <strings.h>" J( W! e1 o" I( T- m
- #include <sys/types.h>
. Z- N' k3 \" Y+ M2 ?5 N9 C - #include <sys/socket.h>
' Y% K. i0 @; Y5 a& P0 u3 u0 v; |$ _ - #include <arpa/inet.h>
0 o) J' Q5 W+ B* Z8 G% P - #include <netinet/in.h>, c) h1 U" V: d% V& j* m7 u
- int main()8 r1 W7 K2 q# G6 b
- {
: H* z; k. Y; I/ T - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
3 P( ^" x7 |# X: L0 h2 n" G; x+ R0 ~ - if (sockfd < 0)- l8 S+ y5 X7 C& ^
- { q! m7 m, w2 ?+ `8 L: ?: ^/ k
- perror("socket");
; I0 G: z8 E+ k! {( i- S8 U - return -1;6 A2 L9 _* n% [2 @9 H" g& W
- } //创建失败的错误处理% z9 u9 M6 v: y9 w2 d% }7 Y
- printf("socket..............% A3 j- K+ ~ O- _
- "); //成功则打印“socket。。。。”
7 F" O7 a8 ]0 a$ E -
/ m# @0 H! p9 L' \# o& Q; x - struct sockaddr_in myaddr; //创建“我的地址”结构体
4 ^+ W3 y; Z6 g - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
- X) A* b% }+ ]$ T: r - myaddr.sin_family = AF_INET; //选择IPV4地址类型* b/ K3 o& X$ Q6 f4 g7 n q
- myaddr.sin_port = htons(8888); //选择端口号
( ^8 R Z- u. ~) F- `- l - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址& J# y2 `" w$ K3 r5 b: y: }! q- g
- $ D6 ?9 ]) j, x6 L
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
q0 R# R6 `8 H) D3 B0 ` - {
0 q; e" G6 H5 g. H( x4 @ - perror("bind");
8 {$ u) p1 s5 |+ c- S - return -1;+ }. {, n5 r. {1 R7 T; \9 H
- }
. z" W5 {2 K( }. G* s+ {% r" ]$ A - printf("bind..........
) s F' y% h! ?0 A1 u1 L' ] `3 w - ");. e% Y/ X9 l8 ~( X% |" L5 w
-
8 Z' U9 j2 ~' [( Y( ^; S$ a - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
5 V: V# h, V" U! r. v: M7 P( M - {- @) @; S8 Z+ x c$ {' J5 j# v* z! J
- perror("listen");
0 k8 x; \( D. d- H. H0 J7 _5 K% K* O - return -1;
/ \% n: r" @* i ?( w! p- \ - }
; C* t' J) J3 @6 J9 W O7 Q8 \# X l - printf("listen............4 J# `0 A! B5 R$ c
- ");1 [7 Q# ~: G% r2 E9 [1 I
- & G6 c6 w' S6 a( t7 D9 H
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求 }0 c& K' }: h: H! E
- if (connfd < 0)! l7 g4 U6 c) _5 |/ P0 J) I) t5 v% l
- {
9 N, l# W9 d# ~ - perror("accept");
) q0 U1 x" v' V6 V# {& D# p6 J - return -1;
: v6 i [7 F; G" e6 ] - }
0 e, |- i% x1 D/ E! M - printf("accept..............
3 T9 D& j2 K, r - ");* C% X$ S+ r. V8 I1 F P0 N; f; L
- char buf[100];//定义一个数组用来存储接收到的数据- Z2 }- z' H3 L4 o
- int ret;' D' O, x7 u2 D. P1 h
- while (1)( y% R" M M" j& h! J* {1 `8 ?
- { E \3 ^4 ?: b/ u! Q
- memset(buf, 0, sizeof(buf));* I" u% r4 P; L! T) i
- ret = read(connfd, buf, sizeof(buf));
2 U" D: q3 Z( [# W - if (0 > ret)
O! _0 B& V7 i0 e - {
, @" x0 C E `0 _; _& } {+ l - perror("read");& q \+ T* t2 \' n1 U! {5 }. B
- break;* W0 I" a% D# J; W0 X
- }//执行while循环读取数据,当9 @) h' I; t* v$ f. B
- else if (0 == ret)
4 @) j4 i& |5 b: k3 T5 d - {
7 W: l6 s" l: } - printf("write close!
3 X2 j# ^" i3 y7 k - ");4 i9 [ @: U' b. f
- break;
2 G9 O( x; N1 I0 a3 ^/ } - }
/ A! M7 L5 h/ T% m3 r# s) I; @& B - printf("recv: ");/ F9 _: m$ g$ T) c& W/ s! j5 c
- fputs(buf, stdout);//打印接收到的数据3 X% {5 ]5 U) E: H4 p% }4 P# s) S. J
- }, _" _3 \# Q* \9 q$ }+ [. X. J6 A
- close(sockfd);//关闭套接字' k) P- u# N/ Y) f2 u& g" S/ u
- close(connfd);//断开连接
$ v) r; e0 s: `1 F - return 0;8 t& ~, n! s' J
- }
复制代码
2 y" `: I6 W# a* `! H
/ w+ y- C. M( n- i% C0 k- /*客户端*/(具体功能和服务器一样,所以不再加注释)
5 n: c6 G* j2 @, ?' K# f( t - #include <stdio.h>4 m- r8 S( _. E
- #include <string.h>
: U. K9 r& O- f - #include <stdlib.h>% n6 s8 U5 {1 Y
- #include <strings.h>
# i5 `( S5 O% \4 i+ R - #include <sys/types.h>
8 o6 H" a* h" E: t* ]1 O - #include <sys/socket.h>
9 x, t2 k" X1 k9 J4 \& | - #include <netinet/in.h>
+ I$ w. y* Q" K) E5 w+ m - #include <arpa/inet.h>
6 M; v8 @% u$ S - int main()- [7 I& N0 M/ u' P- s- v
- {/ t6 c7 Q) h+ L; o! E$ q, z5 d
- int sockfd;
% e$ n. @& U( L+ w1 [' L$ m w - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
- v1 L* I% m6 x9 b' H" s0 U - {
' R5 E. M: \! s* _ K9 R - perror("socket");# I! W8 C3 C5 s$ L- D/ Y4 }: s
- return -1;
) M9 }9 o: b9 ^; j& y8 ] - }
5 @ l1 _( Y$ F& ?) b9 l - printf("socket...........
7 x, h. h" v" \' h6 i2 j; l - ");4 \# a: B! `, ? q) Y, b9 a5 I
-
% z' E+ a# ^, e" l# x - struct sockaddr_in srv_addr;& @# t* {1 c, A t4 U
- memset(&srv_addr, 0, sizeof(srv_addr));5 q# n" q) p7 h. L X6 I
- srv_addr.sin_family = AF_INET;" a! }# _5 |9 m% n9 A h
- srv_addr.sin_port = htons(8888);0 W% h" }- R' K2 q" E
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
7 |/ ~# L0 _2 x, | - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
8 J# D8 E2 ~- Z% b- h0 k - {
( |3 S: U( V' Z$ |5 [, r: n - perror("connect");
0 G! C& N: P# y$ R3 C - return -1; //exit //pthread_exit2 B( R, K! T; S- Z
- }
3 v" s* Z" f* ^ \" d - printf("connect..............
0 Q: W1 h# M+ ]+ g) i6 G - ");$ [& Q! o! x/ ?8 M+ D t
- char buf[100];4 k! A- x1 x7 f; w. Z0 ^; q7 O* r
- int ret;( C( ?6 @: G: Z* t; P
- while (1)
! E& z; e1 G8 Q/ Z8 Y0 q" x6 i - {
+ C8 k2 q0 @& Q- y) c/ L - printf("send: ");
" M+ \: O; ^1 D: N - fgets(buf, sizeof(buf), stdin);
6 |9 p6 V5 Y2 x# b2 E3 U) Y - ret = write(sockfd, buf, sizeof(buf));) V3 T* h/ K" }* @
- if (ret < 0)4 ]$ h) ?9 \5 \( u5 Z; j: _5 V
- {
3 ?1 \5 I+ f1 B6 r! L - perror("write");
8 M/ H |% K; E - break;0 C- _1 P& }: F# }9 f
- }4 K! w) O4 z; }% j6 e! j
- if (strncmp(buf, "quit", 4) == 0)$ ?* q# O: q) m3 v8 q- c& O
- break;) l: n8 s6 t8 y* a/ m; Z. C
- }
, \- U& e4 B- I: I( O. v2 I) w - close(sockfd);: J: `' Q! Z) e- @2 e8 ?/ J2 C
- return 0;9 T9 b+ _3 Y/ s! A) a, f
- }
复制代码
( o7 \! a) s% C4 Q# m. v! Y- K( ]5 L t
|
|