管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。. w- A8 t( A: V; V8 |4 \4 S2 _) ~
/ {8 J. V p# k9 c3 o0 W6 ~* K
9 j' v* v/ {* k3 G! ]& G1 y( Wsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
1 M9 x# p# V# X, Q+ \' p2 G# C6 \! n4 m( Q! F- i
& m: |6 h0 y' Q/ H3 qTCP协议
( b3 q5 o0 n% r( Z6 t# {7 a. e5 UTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。: o) R0 R1 h F0 w5 w- ^$ q
4 \. F3 R* U! g: ?+ X" T+ c) w% {( R4 r! d8 ?! D
关键词:三次握手,可靠,基于字节流。
9 `% w; n/ ^+ m2 K# ?7 B; e' c. V' ^% ]2 X n
- E# b$ A0 B4 I' S9 I, Y* y
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
8 ?3 U. |; ]% V8 S
3 i' m0 O. ^, s; rTCP服务器端和客户端的运行流程1 ]3 A4 X ?" `2 A# k9 S
% W+ h5 z0 q+ v& @2 u/ [ z: V. }; j$ u/ n# E# ]+ C/ {5 H
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?2 s5 d+ C( k) m% u
5 j7 {" b! ?: T Y# b! d c" j* P+ f8 ]3 v5 Z
1.创建socket1 L7 I5 m6 \0 C" W6 l; x9 I8 @- v
socket是一个结构体,被创建在内核中
0 u5 i; Y7 M* q' A& J+ _6 J# @ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议! c; @5 g6 J( G& T% @0 F" E/ }
' P, N$ e, G: }
& g" R- q3 }% Q# d \; z0 |2.调用bind函数
: F% E k; r& Y+ ] 将socket和地址(包括ip、port)绑定。4 g- I. U; q9 Q
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
, X0 N$ z$ C# K struct sockaddr_in myaddr; //地址结构体" s) J( F. e6 N* { R: \
bind函数& B: t9 h; W* {
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
, S! o8 a5 h. a2 ? j/ Y
# Z. h; w( O( ^* C; y. w& m u" S
3.listen监听,将接收到的客户端连接放入队列% g# b6 L' W- [0 E; p7 X7 T
listen(sockfd,8) //第二个参数是队列长度& f, ]2 T$ e- W2 j+ e+ j
1 k# E. t" ^1 b0 m2 q# A* I' ^4 s
* z$ P0 I A& K2 p$ c- }4.调用accept函数,从队列获取请求,返回socket描 述符$ Z& V6 x2 ]" P& Y7 k
如果无请求,将会阻塞,直到获得连接
7 ?! O8 L3 h6 o4 d int fd=accept(sockfd, NULL,NULL);//这边采用默认参数6 T. L; g! o% @3 o7 \( n
% _ N5 t" [& @6 L. R( G; ]' }( X/ W% U r3 {
5.调用read/write进行双向通信! s i4 X& F# |. w: w+ L- _( Y# Z
& V x2 U8 e0 ^
: c4 W" g4 n" F" n+ z6.关闭accept返回的socket
- e: O* G1 u9 [' w/ P close(scokfd);
) r! H; g5 e+ P* }1 \8 v9 l9 c* d+ d$ f- M6 u1 [! H0 c
" u) {) o7 ?: R2 ^
8 t: b& V( w0 i3 N! ^1 L& q9 x$ m3 }5 F6 }
下面放出完整代码
+ `& w: r( v' l6 X
- A1 y* m) z& p0 C" _" O. N- /*服务器*/0 l( E) h3 M6 {/ T% P
- #include <stdio.h>
" U! A5 v) Z& d1 n j( L- |/ [, b8 r - #include <string.h>! x; X4 I% p% r# [" h. E+ r
- #include <stdlib.h>
4 X) U$ P/ Q7 t8 k - #include <strings.h>5 p! O0 `) _% Q4 r
- #include <sys/types.h>
& a# f1 h/ v$ g1 X' e3 W$ I - #include <sys/socket.h>$ v0 m, Z" R. @4 D1 O' j) P0 @
- #include <arpa/inet.h>9 @* _/ _ V8 D" K3 \* S
- #include <netinet/in.h>
* d6 r3 ^, u' x+ R: Z) D$ a - int main()
4 a4 v. u. Z$ ~" w - {
: P8 U* x1 ]2 Y5 z - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字, \7 T0 Y3 s2 j1 H7 P4 }
- if (sockfd < 0)0 `% Z( c9 |! b# L; P
- {# M/ |- {2 B* a: `
- perror("socket");
) _1 M4 y* W( U - return -1;' V. D' ^( Y) ]5 I6 J& Z
- } //创建失败的错误处理
1 J; j9 u' H+ C2 \ - printf("socket..............
3 _# I; B7 p. `5 q; u) K; f) X - "); //成功则打印“socket。。。。”* g! n' f0 }6 ]% R
- 5 F7 E) C" E% [% M; i3 Y* N8 C
- struct sockaddr_in myaddr; //创建“我的地址”结构体! h8 I( l( B/ u( E, j5 T4 d
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
6 N: v! s8 ^" s8 J. J d* j y( n - myaddr.sin_family = AF_INET; //选择IPV4地址类型
. p8 b! |' p; ^; Q6 u; o4 G - myaddr.sin_port = htons(8888); //选择端口号
: y( U T' J; X5 W7 c - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
- o) q; o$ W" e9 Z* B - " V/ L$ h% P2 c. {0 ]! y
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
% J d, X/ z) c7 k [ - {
' _1 a: m6 z3 @ - perror("bind");$ L' X$ d& R& N- W
- return -1;
+ o1 {6 I+ p y5 \, l5 y - }0 L( ~+ Z j( C% a. l
- printf("bind..........1 r, T3 u% I6 r2 y- d6 V
- ");# K5 G- P+ C- h3 U5 d
- : ~, C" g0 {; I/ h
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- \% c t$ x3 h6 r/ L
- {
7 i6 ]& ~- T# E& Y/ Y - perror("listen");
t/ ]+ ]1 K+ L+ L- I8 f5 { - return -1;& j, }! h2 c; y& o! | q
- }
& z- {2 w( t# a - printf("listen............
2 P& K: \! F: n4 w - ");" S# `! }, N3 D
- ( P8 V" @! V# G5 r6 k+ T/ n
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求, i' Q0 d0 y2 R( ~ ]- e: @
- if (connfd < 0)
" P0 F2 |1 N( w8 P - {
# R, A% W" I P+ l0 |1 D - perror("accept");$ ~* o3 t2 B7 a: [. B, p
- return -1;
( T# L7 b c. f# @5 A) k6 E - }
* u$ ]% ?2 V- _, t/ ] - printf("accept..............$ D1 d4 l! n) K0 K* `( Z* ^ j: E5 M
- ");
9 c% Q, r& S: d; r. j' i4 E - char buf[100];//定义一个数组用来存储接收到的数据
8 g& @! d$ ^! R. c! n0 K+ w - int ret;5 n! d" G* ?5 H7 h0 c
- while (1)8 ^* \9 J* }6 }" | _ S6 s$ E
- {
- _' W/ _7 Q1 h# A - memset(buf, 0, sizeof(buf));1 V! Y) j# }! L" s7 C8 d7 O
- ret = read(connfd, buf, sizeof(buf));
+ w! {& m! _4 p2 g2 G/ u! X - if (0 > ret)
( g0 y d4 n8 G+ p; W' u4 ^/ Z) i; ^. T - {
. m' L; Y8 p, O/ s0 K* V - perror("read");( j2 v5 s. I- Z' y# X
- break;9 |4 u, B) i5 u6 U: `' x# M
- }//执行while循环读取数据,当
2 b7 C: w* [5 V" h - else if (0 == ret); v7 l3 H; |% W3 {5 g1 K
- {4 k0 m+ {9 ^, n- ^7 l- h% g
- printf("write close!
1 n/ F) o9 j/ q; }: E - ");
. A8 S) {5 H4 G) u - break;! Z* S6 I- { m, Z, [/ ~
- }
# S, C$ g* x6 T! p5 b - printf("recv: ");
, u" H! a$ M* G8 C% o - fputs(buf, stdout);//打印接收到的数据
: k& Z/ `* ^+ q9 n - }
- L o( Y3 @' [$ o d0 v - close(sockfd);//关闭套接字
T/ d' b( L# r# p i# e. v - close(connfd);//断开连接( q+ f* N( {/ K& u3 F7 }* K
- return 0;0 \' g( M0 u I3 u
- }
复制代码
' Y6 w+ G- Z: S0 i* s5 w
* |8 j+ s( u; ]3 W6 n- /*客户端*/(具体功能和服务器一样,所以不再加注释)' V/ s4 \6 L2 f& Q: [6 P8 h
- #include <stdio.h>9 v! ]7 y' O8 @: B0 Q
- #include <string.h>1 M# I8 `: Z: E, ^! I
- #include <stdlib.h>6 p% N6 k4 Q* G' w& s/ j
- #include <strings.h>
2 K; l. { k( h1 }0 b) S - #include <sys/types.h>+ ^$ J8 S2 a0 ]7 }% X' K8 Y/ H! z+ @
- #include <sys/socket.h>5 Q6 r4 X+ z4 W5 I3 L; f5 l ]
- #include <netinet/in.h>
f' a* G( [6 T9 l3 s - #include <arpa/inet.h>
( R2 F2 G% ~. H, s - int main(), l' o' u" c2 P: z
- {7 x6 |0 R Z* P/ a, C2 z" |$ f$ ?
- int sockfd;8 ~4 `5 n" m# |. A& f7 }6 @, b
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( k( b8 x' T$ X5 x- g; a
- {
" [, `3 N9 i# U4 N - perror("socket");9 }4 V6 N4 e1 t8 m
- return -1;
8 V& q9 L4 {% c: [; P - }3 C5 ?/ o Y# m* w1 ^7 A
- printf("socket..........." w# n0 J U; ~ o
- ");
2 m0 e: A) O M. l2 b -
0 q2 @1 \: p- |- s8 a6 p3 m. \ - struct sockaddr_in srv_addr;4 }; ^) \8 ~5 v" P; C4 g5 }, V8 V
- memset(&srv_addr, 0, sizeof(srv_addr));
/ Z1 A* {$ W) L. J3 Q1 G0 t - srv_addr.sin_family = AF_INET; Y r9 S4 @" E. H& U
- srv_addr.sin_port = htons(8888);* b0 _- I) Q3 Z' _) l% r
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
' P; V! c; j' w' |8 o4 ^ - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
+ A6 C+ A# t9 {% M% G8 F - {/ g' s9 | k" _; K4 S: n- q% J
- perror("connect");
) K3 Y d5 M. @4 @! P7 S - return -1; //exit //pthread_exit4 i' {3 ?3 _( A* i T
- }+ h; P b3 m1 r+ X0 V2 K6 Z
- printf("connect..............0 b$ T& z8 a; H4 l
- ");; v0 R! [, o8 O" Z# i
- char buf[100];$ |! g4 e6 z3 Y" p. U( N+ C& ^, T
- int ret;
' s/ d* s) D% z - while (1)! d& g& w8 G3 v% @2 o
- {
; b6 B$ D0 f9 ^. V4 ~6 W - printf("send: ");
# o" S1 Y; V9 _0 W - fgets(buf, sizeof(buf), stdin);
1 N9 D" l$ `+ E8 F' T6 i# j0 l2 c - ret = write(sockfd, buf, sizeof(buf));: v+ g$ D1 F& w% p# J* V/ X* l+ O# L
- if (ret < 0)& J- m8 \0 b! Y& l; Q
- {
" t* x0 o6 j" i# j0 ]8 F - perror("write");7 g$ C) m+ V8 ], y; Z
- break;
8 y* j9 _" e; u8 _$ @9 r; O - }6 ^- [5 p- F0 e( k2 T# p+ e" e
- if (strncmp(buf, "quit", 4) == 0)7 _0 | v M8 ^" b& R. {
- break;
8 w5 ]6 k5 t4 V- {. [ - }
" }4 s4 |4 l5 f - close(sockfd);
) ~. R5 `+ H! `5 L& [ - return 0;
* a* b5 c! K: Y6 C - }
复制代码
( ~; F! G" |2 R/ ^1 A9 m$ G' f e9 e* r6 l4 T' m
|
|