管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。: _$ v& F. R2 W( n' y* u+ q
* g- R! i& \9 K1 o; n+ b- S! I0 ?+ @& m& Y, Y7 [) _
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. c- h/ t/ c h8 b. L; s
# k: W* x, h# r0 r! ?+ W! v! E
" {/ O- X: b9 O% Y2 n3 P
TCP协议
7 R& p' E# j; [TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
5 _( U* G$ K4 q- P3 G
6 b& s& a* P; ~; K, u* f# `7 q0 K- h
关键词:三次握手,可靠,基于字节流。
8 i, z& x% A5 @& e8 \/ h B+ b* j; c
% _8 [6 x2 ]& e, X3 \' Y可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ l" ]" K( j. T4 K8 D
% x& e+ K: W; ]4 ?# r/ p
TCP服务器端和客户端的运行流程
& F0 A4 r# U& R" _, q3 b
; z1 N& F2 s( F% T3 f! O$ M3 x5 {, N7 S6 |9 D4 c( T! U8 o; F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?" p* t( t& M8 X. N$ Y6 b3 M
) p, X" B; h. m! _4 K
# L8 A# ?7 V3 R% w0 H' y
1.创建socket
1 _" ]8 G# M3 E* v$ f, V8 I socket是一个结构体,被创建在内核中
# p+ j) y! _ c sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
/ A+ O, q. w& F( L k( N
O, i: |* R( D9 \8 _! l: @ Y! ~3 ~5 o: B2 D6 M$ G" I3 K
2.调用bind函数4 P! v9 ~8 v& `. U3 |
将socket和地址(包括ip、port)绑定。+ l8 K$ `# u3 j6 ~, m, O: i* H
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
4 c: N- T1 x T& i0 W' g struct sockaddr_in myaddr; //地址结构体
; R/ Z: Q. n. ?. Z! ^; T" q: R _$ r9 r bind函数3 l* s9 T' Q2 \% _; f- }
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
3 E1 T `+ R3 G
& x$ }" I: }$ {* v$ i, }
! \* J" N% e& V c9 w% H3.listen监听,将接收到的客户端连接放入队列
8 S5 r N7 e, s* ~ listen(sockfd,8) //第二个参数是队列长度$ W ?# V- P& R6 @
' v4 g' L* I7 I% m2 M! \+ Y, M! I7 \8 _4 A
4.调用accept函数,从队列获取请求,返回socket描 述符
8 x2 U. v2 I: e. \6 ^+ z 如果无请求,将会阻塞,直到获得连接
: U3 H9 N3 {7 `1 C1 E int fd=accept(sockfd, NULL,NULL);//这边采用默认参数* @$ m0 E/ ?$ O5 E
8 e$ m2 r3 ]' I; E5 d* x" q
* \1 ?& P7 ]) A: N5.调用read/write进行双向通信, a- H) q! D2 l# E. s) @
* Z" w7 }( @, ^% M) [- u7 i
$ p8 l( [% y+ L; y6 }6.关闭accept返回的socket
6 K) O, o7 N. e ]( c" b$ L close(scokfd);8 D% r: U0 o) u! [6 A
: J5 A9 A1 a; u9 I: t# b
6 |. W+ b+ ?& l [; O0 E; X/ Z# q0 F5 r6 ^
9 i- _8 U4 p9 S! |; R6 G: _
下面放出完整代码! M/ u8 E- q# c7 W3 W% o9 a
6 N, {5 Z0 A6 w- /*服务器*/( b! \ Q6 y- Q5 b& x
- #include <stdio.h>
7 ~/ ~( X0 m$ ~0 U - #include <string.h># |: K" \* K% H* R5 d2 k$ T
- #include <stdlib.h>
8 y% h# L) m& o/ Y% ^5 i' Y - #include <strings.h>/ ]. k1 Y" h, I
- #include <sys/types.h>
- \; Q1 {+ U9 w8 A' _6 X - #include <sys/socket.h>
3 z% D' f0 z3 ~* Q" h- a% y$ n) }: x - #include <arpa/inet.h>
$ K g; i9 }+ R" r$ `6 c - #include <netinet/in.h>
% V+ L8 L: C9 r - int main()
. ~* `' N- ~( B9 K) b - {
) K. C: l6 s1 w8 T* |' W8 r4 w! m - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字' ?5 }# @8 K# V l9 T" e
- if (sockfd < 0)
' O) S, Y( s' c$ X5 } - {3 Q0 c+ i+ ?" A, e# L0 `* n5 z
- perror("socket");' \* p1 \' g: z6 u: J% o
- return -1;2 A/ B5 O5 k* U6 w& A |+ t0 i
- } //创建失败的错误处理
" Q4 r% A6 i4 O. S2 P2 j( N4 o - printf("socket..............
: ^/ a- u. z' _3 L0 @. F; b( a5 o: d - "); //成功则打印“socket。。。。”
8 e7 W/ l* \8 O' l/ K - j$ ?5 g9 ~6 w& `
- struct sockaddr_in myaddr; //创建“我的地址”结构体
( c9 m A: d$ Y - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% H; b( L$ f& I
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
' ?5 q. {& p y0 H6 i4 Z1 \* k5 C7 ` - myaddr.sin_port = htons(8888); //选择端口号
& E1 J4 L, T( o$ j - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址/ B: T. c1 P- t* C
-
+ ?% g2 A2 W- u7 [- V* ~ Q - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字1 j! |2 j& v. H; R
- {
; k P8 }$ G8 M2 Q2 L - perror("bind");
( f1 P) i; Y- p! U" J - return -1;
" X! l- i1 j$ |4 F Q - }
* S; B) `/ b% M1 B! k$ [ - printf("bind..........
( ^% T0 U0 ~* g; U4 C - ");
6 `( W9 a9 i/ h1 T$ B - ) z5 A. R* ?% G2 M
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
! k* M/ v% l4 t& Q, O7 @8 A - {. Q0 M, h6 ?( }4 Z' m% i; ]
- perror("listen");8 q% P! U" f4 A4 t z, e* O
- return -1;
9 }% y: A j6 {5 N! v9 } - }" w5 ~/ [( ~ h" A
- printf("listen............6 y* K$ U9 f* R0 V- o6 O! K! b; `
- ");
5 C" M* y$ t( u6 q -
/ `6 y9 C8 H1 M0 n - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
5 Y$ `* _' T1 H u1 Q! G - if (connfd < 0)2 e9 u7 W: }. ?- W4 ~3 O
- {# X: C( Z2 c9 H
- perror("accept");
) t2 }0 P# w& S/ N5 ]. | - return -1;* \1 w& y$ b% o1 N7 h8 T
- }
/ K) U9 G$ M y& S2 s! E5 W/ D - printf("accept..............' c/ H, m. {0 M1 w5 b
- ");7 @/ m. g* t' _9 u# B5 P
- char buf[100];//定义一个数组用来存储接收到的数据/ `0 d @* A% X) U
- int ret;
) X7 c) I) }; i+ r: k - while (1)2 J( B% ^) A6 |
- {4 q1 c" U& f& K0 g# P- {
- memset(buf, 0, sizeof(buf));
( `) z }; [5 S9 w5 X - ret = read(connfd, buf, sizeof(buf));1 F( r0 a( [! r- i' @
- if (0 > ret)' l; V# X o& M2 e- a' h7 n
- {* X% F- Q, v$ P
- perror("read");
. X3 n$ g6 d: U! R1 }1 r- f4 i - break;
@+ q9 d6 t$ \6 t% M4 x# d$ ~ - }//执行while循环读取数据,当. Z* d. B" p+ e& u, Q
- else if (0 == ret)
& O& ]0 ]" n U# d" E - {. P! S0 p; g. X D: j
- printf("write close!* ~! [' Q/ r& E* ]; c& | l
- ");
5 q- }; X$ H- _: Y - break;
6 d# }7 K0 p& |( l - }! ~: Q, |4 f: k
- printf("recv: ");
* v y; x: `+ `/ d1 T$ @ - fputs(buf, stdout);//打印接收到的数据- x! Z0 d$ F L2 P
- }
0 H3 b; q6 [0 o - close(sockfd);//关闭套接字* ]0 h/ A% r7 Z" q
- close(connfd);//断开连接; n& I2 h: {& k( [2 g
- return 0;
5 ~( x f1 t- s- ?/ S5 U* Q - }
复制代码
" Z) Z% _# Q9 d+ `0 j6 n6 n4 |7 C- i3 ~& v9 G4 J; `! z% j
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
" J; n' t! A. e' {9 o9 D8 ?( g - #include <stdio.h>* u' \: r( M. R
- #include <string.h>
# c! d7 {) ^: W4 b& m - #include <stdlib.h> `. Z9 I+ |! ^" @3 d# P
- #include <strings.h>7 U! p+ q8 n1 w/ B$ e' y
- #include <sys/types.h>
2 M& ]9 S+ P4 u. j2 K4 C& q# l - #include <sys/socket.h>! F4 ]$ E8 R& Z$ K, k3 y
- #include <netinet/in.h>
3 X: k$ V# `2 y# H1 a - #include <arpa/inet.h>
( [9 { f9 _3 ~ - int main()
5 B. b! j" o& t/ P - {
- ?& f% e- Z' h3 P" Q - int sockfd;
4 W, N( M9 Q6 U' B5 P) X e' n$ g - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
7 D3 W z( k n- y1 d - {* k3 P# E: Y) y: V. a3 a
- perror("socket");! _$ @4 Q4 a4 X: N* y0 [5 D f4 {
- return -1;
' R/ o5 M: V$ q" g: q - }8 G- T' Z1 Z0 F5 @
- printf("socket...........) ^5 n# `8 q0 L q
- ");6 \( U1 q* Q; Z g. d+ w( W% E; A
-
( f) b Y) z" x, M" c7 j - struct sockaddr_in srv_addr;+ x& J. z8 p' _7 g8 ?# [
- memset(&srv_addr, 0, sizeof(srv_addr));
$ N4 O9 z9 q1 F1 O - srv_addr.sin_family = AF_INET;
+ O/ H2 d( v o, n - srv_addr.sin_port = htons(8888);
+ ]5 G5 `0 e2 Q9 i; [0 S0 O - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
7 J3 p0 M& N" b$ _) O/ S - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))2 ?* ~9 Z; c' ?; c2 F( Y1 }
- {# j" L; I' G0 E8 f. [. S8 v" I- G: D
- perror("connect");5 x- T, S6 `* q2 Q& U$ Q
- return -1; //exit //pthread_exit4 } a/ k) O7 e3 Z% h; }$ E0 n0 Z
- }
; f, O2 V8 T: O8 x' `6 ]/ R - printf("connect............../ _: D4 ?# G+ E% g
- ");, @) f( g4 {8 r% s4 p1 n
- char buf[100];# r4 U# r2 h. t2 o/ u( x3 g
- int ret;
p- f0 V. R! n- z0 @( ] \) {, \ - while (1)' B2 K; R8 e4 V% H
- {
+ D) r; ]- K6 E5 g - printf("send: ");
) e2 m% b, ^5 t$ B6 c - fgets(buf, sizeof(buf), stdin);
7 \2 |$ S* A5 P: } - ret = write(sockfd, buf, sizeof(buf));
: v% G# m7 T8 P9 C) ^2 u - if (ret < 0)
& v* @! r! \- M3 o, ~ z+ J - {
( t" `" |* b# U% M* S - perror("write");) c. }1 L! E( g4 ? _3 U
- break;
6 ^4 j h+ I' d, t. `. k( l - }
5 j; R; t; k) h0 l) ~ - if (strncmp(buf, "quit", 4) == 0)
# |" F. Q4 X9 d) X - break;
" P4 g/ {1 H3 Q - }' P1 x5 I# P4 ]1 C
- close(sockfd);
! ]6 Q7 H: p: i7 ^+ g9 r/ I, A - return 0;$ w6 B" l1 N5 b+ k
- }
复制代码
! p, M" m2 S! c; k( d! {, m1 a% B9 H: I3 k( _( }
|
|