管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。9 y0 Z" p; a6 {$ i' J+ X6 k' B
0 }5 g7 d# ]5 D7 t
; r' {" x' O" ?6 K9 rsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
6 L& q7 L. ]! Z! o/ l% a/ {0 n* Q9 ~3 r
3 o. t( D6 n5 W- tTCP协议
& k# j2 ]0 v, G. A/ f6 D; v& ZTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 w3 A% ~2 X' j4 |6 Y) D) H
9 w9 j6 y( \& L- c/ l/ S& K# Z: A3 h8 b" \" B% w6 U
关键词:三次握手,可靠,基于字节流。
i' b G) X' ?: a0 A+ L/ P8 L4 l4 Q& o* Z: I% l
& T% E1 |0 F* g; ~) o& J
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。. I3 M. \, r- k7 n
: \- W! o7 Y. `* \5 J* |' TTCP服务器端和客户端的运行流程
0 O* A2 L& J( l# [; w h8 ]" C1 Q* S+ ?( E
8 p6 c2 E3 ]0 E5 { p/ R5 G
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
# W% f0 _+ c8 ^5 l2 k$ q; _/ M, X# q, R3 @; I9 ^% o& l/ h
3 D# y( @+ }# F# }, T& S" ]9 W
1.创建socket
' Q4 l$ I+ f$ x( e( J socket是一个结构体,被创建在内核中9 ?. S6 w$ Y2 U5 L
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议 l4 X/ h& p6 T" d6 S1 j8 R
9 E' q7 E- I/ ?0 `) m7 g0 D- F. G% o5 O& S
2.调用bind函数+ R/ v, o M; F
将socket和地址(包括ip、port)绑定。
5 P# m3 n+ Z6 \ v 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序9 L( f7 G# v4 q1 d) m
struct sockaddr_in myaddr; //地址结构体
% H( j* A5 D6 @6 V bind函数
' P t( p! i) ]* g0 W4 S4 C bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))- x1 t6 n7 j- B/ G
# C( R; p- ^2 m9 L, F0 H3 {, O9 x3 H* c( U7 I0 s2 e: A! V
3.listen监听,将接收到的客户端连接放入队列
5 K3 d7 ]8 \! H1 m1 a listen(sockfd,8) //第二个参数是队列长度! c( ]! k. W M( f7 i
9 L! v, f; P+ X7 c) y5 |: c% G( G
% Y* @( a. E, {. ?
4.调用accept函数,从队列获取请求,返回socket描 述符
$ X2 E" C1 I5 U" b" _6 ]- ?- x 如果无请求,将会阻塞,直到获得连接
' ~9 d# a' Q' Q" |$ I9 `1 G int fd=accept(sockfd, NULL,NULL);//这边采用默认参数+ W+ V. R1 `0 Q7 f& z1 O9 ~
2 ^, N: d G! M
+ X& U8 l' e/ t1 U
5.调用read/write进行双向通信
; d6 ~4 Q' y; d- }
. L: @6 t' d7 k
4 H& Y6 s% V' Y( G2 S( B6 V6.关闭accept返回的socket
# }0 x" y6 n; I6 C0 f$ L% f, s' E4 a close(scokfd);
! `/ T+ o: I2 M+ U) q$ d+ a. F7 {4 t" K
4 U g9 z O, d/ M6 [
# j+ M1 [' {) i q
& g2 P f2 ?1 |下面放出完整代码
# A+ M' Q n0 n) U
|6 W/ y+ M0 E8 D- m7 E- /*服务器*/
R$ ]! w R6 k7 E" Z* ^, d9 S - #include <stdio.h>! S6 `1 q& R& V. p
- #include <string.h>
% g. b+ v. ?5 U" b9 o2 Y5 l X$ x/ ^ - #include <stdlib.h>3 z g8 W. h o8 G# l7 j6 `+ k" a
- #include <strings.h>7 E M; O( ]3 [" T2 _8 K! }
- #include <sys/types.h>
3 R1 P+ ]. C: U9 J; a1 ? - #include <sys/socket.h>2 f" G$ ~+ y. } M7 }; t! y+ ^
- #include <arpa/inet.h>0 i- _2 x# N8 A! m# ~
- #include <netinet/in.h>
3 l( q7 V. N6 E+ b/ o# s - int main()$ p+ J1 _3 t3 n# p9 f, C4 ?
- {: ~( T" P" r: U% Z
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
& a# \) G2 } I* ~2 J6 Z - if (sockfd < 0)
, E0 x4 Q9 Q% c- D: K) V - {" E4 h! x" g( J1 _. M
- perror("socket");: r. t& V- b; { B; B9 t3 m3 ~6 a
- return -1;
4 t+ g/ y) L: U. n% u# y" o - } //创建失败的错误处理
, N+ ~7 I! j7 l. ` - printf("socket..............
( G" H" V9 `9 p9 a7 B! L9 h, @/ w4 p - "); //成功则打印“socket。。。。”
3 G" {$ y/ b4 q9 Q0 D# [- h4 E- U - : U6 G( ?, X/ F Q( A$ \" j
- struct sockaddr_in myaddr; //创建“我的地址”结构体4 K" [ J. g; `: l% H5 M4 X$ f
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
! ]3 F+ W! P( ^' o - myaddr.sin_family = AF_INET; //选择IPV4地址类型
: k' a0 I$ `4 V/ y - myaddr.sin_port = htons(8888); //选择端口号
3 g, ?" |6 T4 g6 c. Z; w/ R1 q - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址: l. Z. [, d8 L: s8 m2 C& g) |
-
4 A- P- @' ]3 Z( ~ - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 t1 Z1 d3 k4 f. @6 {/ x
- {9 V! U* w5 w6 m( X/ `9 i3 c
- perror("bind");$ M& @8 |3 o0 [/ P, G4 N
- return -1;
' S$ S W8 v- `0 r - }
* T4 ~, e1 ?) r - printf("bind..........
9 p& U* P9 ]) I4 ` - ");, `* Z! y% Y* z- K9 F5 @* r) V0 ]
- 4 j7 I+ S( W, @$ R+ E
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
: }( \- l; I) ?( @" R - {
* C4 M0 _* `7 R3 B* l5 A) p0 `! x - perror("listen");
3 h7 u& J) r* }2 H; V$ m8 K S - return -1;3 Z0 J$ i& q; U/ d% _1 S
- }
1 D4 S6 T% g I1 r* n9 H - printf("listen............
) W* C+ i( V6 s$ W - ");
; L, H& I/ @. ]. F - 0 Y" \5 z8 N3 M( H: k
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
& E5 i9 ?9 Q, }0 {; d5 J$ i# E2 t/ h - if (connfd < 0)- k0 ~; m& @2 J6 p' m0 s0 k2 X
- {
5 _: Q- L# ^( g! A - perror("accept");; X9 r$ V1 n- G
- return -1;
8 L" ?$ G, q* t9 i' A - }
# x( k1 W8 x$ U) F - printf("accept..............
* c" a6 g' R" {' K+ f @5 {+ N1 u - ");/ W$ O6 b6 j6 y K( S, Q8 B4 S) F
- char buf[100];//定义一个数组用来存储接收到的数据/ Q& v' V* `: J
- int ret;0 j' R: l1 L9 q: \* ^
- while (1)8 ?* N" K/ D. p( _( H3 V
- {+ T4 ]- W& Y5 U& {8 e9 Z I
- memset(buf, 0, sizeof(buf));- H& j z% [& D
- ret = read(connfd, buf, sizeof(buf));
0 Q* T* p1 x- X" c7 @- `1 h - if (0 > ret)
( w4 }5 _- B& } g3 H - {' i$ G4 E3 y! @% z: `7 N. {& o3 \
- perror("read");
5 G; X7 l( j" x, l - break;! V1 w% w; v) ^
- }//执行while循环读取数据,当
+ n, Y0 b9 W% C: R0 n1 J8 o - else if (0 == ret)
, Q3 @, n3 p- ]9 b - {
T; Z5 u. B6 Y% q9 G; y% E) x - printf("write close!
/ T" b5 b. ]5 p* b) A8 y) x, k - ");+ p. B+ A; j" J* D1 P8 s2 E' n6 B
- break;' r- V3 @; H; |* [6 M* O6 l1 M
- }2 r5 K; I6 c6 w: I
- printf("recv: ");
4 Y7 v8 m- b* |5 A# z - fputs(buf, stdout);//打印接收到的数据7 L ~0 b; y4 R. Q! l. K3 \
- }. A) `0 B7 S1 s) o# t# ?
- close(sockfd);//关闭套接字! t/ m" t4 `& D
- close(connfd);//断开连接
, k. _' e& Y( {" d+ F - return 0;; U' h5 ?; k2 @) v1 v0 {3 B6 C
- }
复制代码
7 L1 |, m# c6 T& _6 n
+ Q* ~9 \- E. e, L! g- i6 o! O- /*客户端*/(具体功能和服务器一样,所以不再加注释)
3 u0 P/ \; q+ }/ i% Z - #include <stdio.h>, o: b7 e9 {9 G; x8 G+ d( \
- #include <string.h>' r/ m5 i: V' U
- #include <stdlib.h>
" H! j; _2 l! c% n9 x( Q% a - #include <strings.h>
" q6 U/ y4 R% C2 o - #include <sys/types.h>
0 }$ m$ ~4 [/ \( ] - #include <sys/socket.h>
: F: T4 U6 C9 u) Z. ~ - #include <netinet/in.h>3 t# z. a7 {, s" K$ H. l; { h
- #include <arpa/inet.h>8 P' f, l3 v4 s( a9 m( W) i, p
- int main()
& X# o2 R; E& m) Z( i4 _ - {
/ j7 E5 R8 e8 R' ~- M4 h( y - int sockfd;. C: b+ E! q6 Q: ?- q- e
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))/ n3 o5 `1 {# t. M1 M
- {0 r- w* c$ x0 \# q, s- m
- perror("socket");/ m& _9 w$ q0 E7 x: J
- return -1;
7 Y2 p8 m7 }' ]# Y+ t/ i: c3 ^ - }- k) P* |% |1 `1 I7 f7 M' a) K4 S
- printf("socket...........
: ^9 u- a" @% s f/ U - ");
$ B9 [( m& Q0 w8 }, M9 s' L& b -
9 P( U' d/ c1 B - struct sockaddr_in srv_addr;
) }! l* d2 w4 S( A d* U( S+ j - memset(&srv_addr, 0, sizeof(srv_addr));2 p" D9 D4 A- k1 t/ a% e9 C+ m
- srv_addr.sin_family = AF_INET;$ _' a$ H- l2 d- K# u ^( d
- srv_addr.sin_port = htons(8888);8 D& }) F* Z; p" y7 F
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");- L) ?2 U( c3 @* c
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
! c1 B5 e3 `; b8 i( L - {
5 C# W, q: O% ^9 _ - perror("connect");/ y3 M7 X G; g9 z I
- return -1; //exit //pthread_exit) F/ x5 U/ e/ [ l: @3 s! h; D
- }+ p8 [/ f4 v: R% C5 e0 W
- printf("connect..............
$ c C3 j' a0 ]2 [. O - ");) _" N' S/ G8 A! ?
- char buf[100];
, G2 F4 u6 p/ {( d - int ret;
9 e7 `1 e0 n N2 h# m! [9 C - while (1)
: d+ H) i* ?) P7 L, ]% d, _ - {
2 B4 }) q! T. h5 q! s( r - printf("send: ");7 T4 Q4 p7 ~* `- o
- fgets(buf, sizeof(buf), stdin);3 S" V% o i& V& N
- ret = write(sockfd, buf, sizeof(buf));4 V2 {* c% e3 ?6 E
- if (ret < 0)# h' w" P h0 J z# V) C1 e2 b8 \# W
- {
& ]; b& b2 ^" |- c7 c. A - perror("write");+ F/ R+ R+ z& n* O/ Y
- break;$ F' Z. b. y9 G' {6 \0 T( i: |
- }
- c% k3 x2 |- t1 @3 R3 J - if (strncmp(buf, "quit", 4) == 0)
9 U- E! ~3 y$ o - break;
% k; U7 w e& O$ M0 \ - }& R; `: ?* M7 Y- d
- close(sockfd);
0 \( T _( ] z# S6 H6 ] - return 0;
' p* U! N+ T4 b. v/ u - }
复制代码 + G% _, R$ w5 ^- |9 s
# h0 N0 G$ u6 j+ B4 D9 N$ `6 ?, H |
|