管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
; _0 D0 h8 M' ^& L$ @- B/ W1 I: u8 v! Q
. B" N6 Z% D5 B: K
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 [/ i" T+ `* D" B, D" K: b# [; e1 @: F. U- |2 n# Q+ j" c! z* ~
?' D: m! d. I& h5 \: _" zTCP协议
2 @ v2 w* O' v" bTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 s0 v$ L8 l. p4 B
# `7 g. L! Y+ Z* H) x
6 u" d( i3 R$ h. K) X) P, v关键词:三次握手,可靠,基于字节流。
m/ V4 E1 D+ w' e: }) i# E( z, H
, s6 X6 J6 F, i4 l8 z& b) z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。8 j6 G$ l: A T( z5 e
5 K+ b8 T) K* \- A9 G" lTCP服务器端和客户端的运行流程
( I# A& `& }9 d* z o4 F$ g
! y7 c# _4 r( L# r1 t/ B2 y. P% ^
' U" ` ~3 @$ m& }& z5 m如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
& d2 R1 O9 W; ], @3 u
: }) V9 n- c' p/ e- I9 M7 E' |3 y, r2 K2 F
1.创建socket9 o; Z% a9 w2 k- a
socket是一个结构体,被创建在内核中* R$ a. u# {8 p& r4 O& A6 v Y
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
1 u' [2 e1 R: H f
6 u7 s; F3 R y# M: b+ `
) |: j. e! q+ g0 p) \+ h2.调用bind函数
/ h; ]; C5 u. m% @/ P8 B5 D 将socket和地址(包括ip、port)绑定。8 r3 h) ]0 {' y7 `) s6 q( f3 {/ E
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) l1 K" C6 Q( x; Z% w% i5 E1 X2 W
struct sockaddr_in myaddr; //地址结构体, j2 t! g3 N" L; H
bind函数
3 |: J6 I: f4 o: Y8 h2 t bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)): z" G2 G7 P! T2 G$ f2 K
" J7 h0 T$ v/ C5 K. ?; {# L5 J4 `# Y! f: N: v$ |9 }
3.listen监听,将接收到的客户端连接放入队列8 @1 b, E/ X# A1 r6 ~) ?; n
listen(sockfd,8) //第二个参数是队列长度
# g2 W* u; P" o- E6 x! c) e! b3 ]1 w0 v6 l( n' P3 U
+ F- d3 O( Z! e* {8 ~& I4.调用accept函数,从队列获取请求,返回socket描 述符1 K+ s- O2 V" I8 J0 P0 r
如果无请求,将会阻塞,直到获得连接
1 Q+ S% H1 ?9 A) Y0 E/ @ int fd=accept(sockfd, NULL,NULL);//这边采用默认参数* f0 I+ q$ j# Y2 s* ^' j
0 u* I, r- _1 K4 g0 l1 C
. x# i, x7 i9 k$ b/ d5.调用read/write进行双向通信
, J' e: X. V) E* F4 M6 z8 Z. P! t4 ]2 s; X
6 t1 W+ J% O- H3 c, G* F
6.关闭accept返回的socket; m4 \) ?- k# J* \. k- S6 A
close(scokfd); E- }* P4 a0 H* m& G/ ?" z
# F! J+ b% o; P
5 U) A L& T* t3 ~0 L: U; M3 t) b: a' v5 A& ] a) c# E
( \) {, R2 E l. _9 K# c
下面放出完整代码8 U2 k3 {3 z! c4 @% J. Z* j% D8 C
: _% ?" e$ Q3 P7 a- t# x( ?% f0 d5 G" B
- /*服务器*/0 v9 |; }0 C \5 g
- #include <stdio.h>
& }5 N3 a) n; D - #include <string.h>
5 }, ]% Q! j. V: t$ N - #include <stdlib.h>! c2 j# ~3 F+ \! Y8 u2 H
- #include <strings.h>
C2 _0 P4 v( f) N - #include <sys/types.h>4 y8 r$ ~/ e4 ]$ v+ Q0 H4 y6 y
- #include <sys/socket.h>1 _1 C' D2 k( N) \4 I' o- Y* y
- #include <arpa/inet.h>" c" f( d; F) f h
- #include <netinet/in.h>
) l) m$ D; P/ W. u o% Z2 \ - int main()6 [: B0 [# m! s0 m4 `0 n1 Z
- {. `' b' [; M/ M. u9 L
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ j- i+ C4 Z( H* M5 _& N; r/ ~4 D5 Y
- if (sockfd < 0). c* s7 r% g9 F2 G" A( `# T
- {3 U& d5 a+ r7 Q
- perror("socket");$ r/ U' Q0 K5 j- q( [% v
- return -1;* T. U1 } T! Y' z0 n. D) O, k
- } //创建失败的错误处理
6 e C2 P* i+ o1 A8 t- b" [: S - printf("socket..............
8 A, E( }' Q w4 u' ^; o - "); //成功则打印“socket。。。。”
- ?& [' E& M3 f1 H2 T -
5 [! L; W1 V7 _5 b - struct sockaddr_in myaddr; //创建“我的地址”结构体
4 s3 c" k3 G: Q: ~9 t - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
9 b; C! q) c7 U- t - myaddr.sin_family = AF_INET; //选择IPV4地址类型
$ d6 p, [( [. U% p1 O3 C- x2 f8 b - myaddr.sin_port = htons(8888); //选择端口号
i% n3 \+ X L7 u& n! y! d8 Y - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址& R. R$ K8 R6 ?9 Z2 @
- , |# y C# Q. E5 q0 P" `, n7 K/ u$ c/ [
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字6 V3 y0 w# ^( ]( v( K! g
- {# X6 e; u9 A4 A* S
- perror("bind");6 @8 M4 d! M6 Q3 I3 N* f
- return -1;
7 D3 g; X4 ^% P# u& E3 e& d/ S5 u% n - }( O* q% ~2 _0 e- j5 X
- printf("bind..........) Z$ W) `4 B/ _# L5 K6 o
- ");
$ R' f0 z+ Y' D0 B' v' } - * S) _0 |% F8 i
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听" g3 X4 y& j9 A5 N4 \
- {
t/ N7 V6 }4 D$ O* a - perror("listen");
( A! l n+ _0 K2 I% X+ H0 N& H - return -1;. \* ]; R! f# _- I2 G- w' ^
- }2 q+ }- D* S8 ?2 A9 F. k
- printf("listen............- ?+ V a. S( G5 f5 f) f+ f+ I" ?
- ");
f0 K* o0 U3 B0 T( K2 O -
! T W4 U" |9 R9 M5 B: _ - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
6 ]0 x. i, d9 F - if (connfd < 0)+ a8 B; a0 c% d$ l, U5 t
- {' }: K! b; J3 |' K1 h) }
- perror("accept");
9 L8 @: u' K0 T" M7 e$ H4 T/ H - return -1;. h6 i4 Z3 j$ l
- }
9 q/ H X' u+ C) P q - printf("accept..............
1 S1 o7 D- X2 K7 u+ | - ");
) @" v! c9 ]4 | d7 d - char buf[100];//定义一个数组用来存储接收到的数据# a" F1 T, l; C, H0 ~
- int ret;
9 h1 f4 O8 Q- o$ h5 O E - while (1)% C9 J- m" m0 |7 c$ t# i5 }5 S
- {! ^/ Y. n' D) P4 @4 r1 H" z
- memset(buf, 0, sizeof(buf));1 A( Q2 E" Y$ q$ ]+ ?
- ret = read(connfd, buf, sizeof(buf));
: S% `6 ]* s! a0 q) @ - if (0 > ret), D @" i8 F9 p0 k
- {
' j3 P; W: ^1 X: o9 d - perror("read");7 u8 } D. z, R u
- break;
9 Z) X# J- m8 R0 z3 t2 t - }//执行while循环读取数据,当* h6 d* @9 k: L7 j
- else if (0 == ret)+ a b& W! d" Y0 u- ~
- {
; o; T8 e3 h+ J2 J o ^" b5 M - printf("write close!
5 P% A! u% u. o; w - ");( P7 P+ B& m$ O- |1 _$ |4 S
- break;% n: @3 }/ E' P7 n3 s
- }
6 O: l% U- x3 `! m% U* y, @ - printf("recv: ");
2 o1 w' Q3 o1 L - fputs(buf, stdout);//打印接收到的数据1 P8 L% U0 V9 Z+ I
- }
/ k( {/ Z* f/ Z3 U) e+ d' p7 X - close(sockfd);//关闭套接字9 i" P i; X0 y; `# h# |
- close(connfd);//断开连接. g+ R5 y; Y% a6 ^
- return 0;+ g. {* J. t, u4 j' w% S% H' j
- }
复制代码 " l: |8 ~: }% C
) C" k; n6 F+ {; Z+ c- /*客户端*/(具体功能和服务器一样,所以不再加注释)/ @3 [( V& J8 g* a& i
- #include <stdio.h>
' k; c s- U* `) z, x - #include <string.h>
7 F: x) H, G, w# j* S! f }+ _ - #include <stdlib.h>3 @% F2 l) J' a! ^' B
- #include <strings.h>
+ ^: L" p- Y; L0 X2 j - #include <sys/types.h>) T2 O, W( r, g# k+ I# y
- #include <sys/socket.h>
$ ~! W4 S2 S7 C, t# s - #include <netinet/in.h>/ M9 {- D/ @7 A/ d; B5 F" ]
- #include <arpa/inet.h>$ U5 W) Y% O) a
- int main()
1 v+ g* u( A/ H Z" U - {
T. F0 j S" g3 r Q - int sockfd;
) i& Q$ i3 X ^+ _+ z7 y2 A - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))) j% I3 |+ z) N1 a
- {
2 B2 a; g; h- E0 Z1 V/ [4 _ - perror("socket");
9 B8 p1 P( L: X- R3 X9 g - return -1;
/ o5 u6 t# s* }6 k( @- I - } A# m5 b" A! @0 ]! t$ k* a( {
- printf("socket...........
- q( U+ R) S/ a6 P - ");: K y2 x! w( N2 \$ D
-
) X# N0 t. Y H% i4 T2 h$ x& r - struct sockaddr_in srv_addr;
8 ]9 u, ? R& `+ [( I) v1 ? - memset(&srv_addr, 0, sizeof(srv_addr));
3 q& r& j) _& w - srv_addr.sin_family = AF_INET;
' c7 Q: w7 @8 b% ? - srv_addr.sin_port = htons(8888);, k6 d# V, W" j/ T" U" i
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169"); a7 M% @2 `+ o2 z
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
& \# q. s! t9 l% k, Z - {
& }# ~/ ]( H! P3 H% R. A - perror("connect");" W5 x0 {3 M: p$ d. j6 v( c
- return -1; //exit //pthread_exit
( g! S, A& q/ p$ k5 S9 d0 _7 b# P - }
' c1 N' ^3 r. J; C. z - printf("connect..............+ \0 v: G' D! h, b. \8 U4 u% n1 `
- ");. o6 G1 E1 H. V$ j7 {
- char buf[100];7 V7 h, K( z3 j' e7 l
- int ret;
8 s( W+ |- `, f# J) v* n - while (1)% s7 ~9 d- U* ^ x: r* x1 Q* E* @
- {
8 q, a1 u7 z a2 {/ J! |9 ?" O% Y - printf("send: ");- M( ]9 D) x2 ]5 _5 k; k
- fgets(buf, sizeof(buf), stdin);
* r: U; e. Z2 l o7 {/ _. S' B - ret = write(sockfd, buf, sizeof(buf));0 p# ]4 f, k8 J, t- ~# U
- if (ret < 0)# @3 M: r" c4 U) [# Z
- {
- S2 Y% Q K+ H) h - perror("write");7 E3 z# C# D9 W# {
- break;
0 W# m6 \% k$ [/ k" h* M: h - }
0 q' n% M% @; X/ ] - if (strncmp(buf, "quit", 4) == 0)9 O. z5 O" ?0 i2 x$ p D
- break;0 c5 l+ d7 ^7 c
- }: A) T& B$ Z, G2 q/ O
- close(sockfd);
% @) d6 M! F. f9 q, I0 u; g6 ` - return 0;
+ |+ p0 \8 ~; n. u - }
复制代码 ; ^7 b( D% M" Z1 i
" |- r3 g. F9 \
|
|