管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。) Z8 {7 ]' O5 e, F+ A' l
9 U% |, C6 f5 j. E; M7 G
( D3 |8 r+ @. `- M) V" u: rsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。 _. _+ u0 @* c0 U- F% Z: M
' B/ K, b8 b7 a4 F
, G6 [5 O# }* D8 X
TCP协议$ U4 i1 ^1 T1 R2 n, v) ^2 a4 g
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; [ r {9 i3 O! `/ B& E, e4 w! Q9 J! |% _1 c; E8 a
4 M5 F1 M: ^) z; X关键词:三次握手,可靠,基于字节流。
: ~6 E' f( F% S6 r
2 L. k4 l' i u" C+ Z% [9 h* u; M. g7 q
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。/ T7 S$ s4 Y5 S* w4 F& {
" J2 I; X- [' B ~) Q7 MTCP服务器端和客户端的运行流程
- T y/ ~* M1 Y
, E+ w; S" V* w2 n2 U, [! R6 D0 e) u' ?: G0 I
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
( ^% ]3 ]8 L1 q! q9 \, j' S ]4 U+ ~8 ^, {: |
8 k t C# q) @1.创建socket
" ? J; R- z+ n9 f* m3 m socket是一个结构体,被创建在内核中
! l$ F% Y7 j. u. Z) Z5 V6 q) E3 Z sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
$ J1 @: r- B* x Q5 P7 q
- R" d0 |8 }5 s% ]0 L
; y& L0 E+ c" Q, K9 s& d2.调用bind函数" _, P* ?) b3 j2 z# U4 O4 {
将socket和地址(包括ip、port)绑定。9 n! i1 L# t6 f! P
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序8 P9 K% U& ?1 `% A5 E8 e9 |& G
struct sockaddr_in myaddr; //地址结构体
0 f8 J$ X. I; o* \ bind函数7 D# }. G- i+ ?+ B* X: L
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))6 K7 H+ E' ]; S) T( A
2 M6 Q5 l+ u' R! }
9 I. @+ v7 Z# C! S) |- U8 ?3.listen监听,将接收到的客户端连接放入队列
) R# j z9 c4 z5 w listen(sockfd,8) //第二个参数是队列长度7 y& M0 D. O# r O0 U8 |
4 n; Y" j' p9 \6 P* S
7 T0 V3 O9 _: i; h2 b
4.调用accept函数,从队列获取请求,返回socket描 述符
! ^" Q. o( O m; @. n' M: ] 如果无请求,将会阻塞,直到获得连接
& C2 Z* A( l5 E& e" ^2 s2 F$ x" v int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 `4 c8 b! j; I1 ]0 |0 Y/ a
2 X f- q6 l+ v7 Y
# h( `! d: g" \ T0 |5.调用read/write进行双向通信& p5 [. ^& d j# o. E1 [
% x6 d, x& _% w" t9 Q0 ?& _
/ `$ D# g s0 P n7 f6.关闭accept返回的socket
" {5 l5 c ^: z9 ]8 | close(scokfd);" M4 n P# Q3 m& d8 \
9 L: n, O2 ^- |0 |9 n5 q
& ^% l+ x8 n }
( w; k* c D0 D+ d$ E% [4 d D9 S$ e; c3 C0 O
下面放出完整代码8 e* A- x. F. g" P
) i1 w% w R; D- h; v' ]* g- /*服务器*/' v4 V" u0 w3 r& {/ m# p4 R
- #include <stdio.h>
" L9 E* S! e; O0 Q: G6 W - #include <string.h>) S6 T% k* J. T2 a) q
- #include <stdlib.h>% ~5 c/ }& e; f9 t( p
- #include <strings.h>
- d, A4 R# v) \ - #include <sys/types.h>7 E4 b9 o- c+ `+ v% {. n H
- #include <sys/socket.h>
3 n8 r" r) v# G7 G7 b2 P& T2 z5 c - #include <arpa/inet.h>
: _5 g3 e4 n" H) L+ z9 ~ - #include <netinet/in.h>
7 Q' L" g: P2 j+ e0 t - int main()3 R; t3 Y. W: v: z7 h }
- {3 I+ e4 H) w- u9 L2 ~- t0 Y
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" [6 V4 e( p6 z# i/ I: J
- if (sockfd < 0)
* ^0 ]/ k/ ~' R6 \2 ~ - {9 y6 ]; `: \% Q7 m2 }
- perror("socket");
6 r& O! ?7 d6 ?* q% ^ - return -1;5 E8 }6 |7 V; q0 W Y2 n, Q: M
- } //创建失败的错误处理
& z5 b% E9 n( p a' C% @0 ` - printf("socket..............
! P* m* C: ~: S - "); //成功则打印“socket。。。。”% B4 Z- [* _4 F6 S% |
- 0 P* h$ A, P: L
- struct sockaddr_in myaddr; //创建“我的地址”结构体
8 `5 a8 S: [3 M0 ^/ q6 j& f0 N - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
1 p) V1 q7 C6 V+ \$ R3 W - myaddr.sin_family = AF_INET; //选择IPV4地址类型! _7 J" _* r5 a
- myaddr.sin_port = htons(8888); //选择端口号
! y. U; V+ z( N - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址; _, @ x/ q7 A% U3 ?; t' x
-
& P* b! c: b+ g" z5 N; Z - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
: x1 Q9 w) T' R; Z1 y4 G - {0 x T2 y1 B1 x( P; Q
- perror("bind");
. r6 Y. q4 A2 |. s1 n - return -1;# |8 T& \0 i- G7 g, o# I5 y
- }1 L4 z8 G+ z: m; Z, ?+ H3 X
- printf("bind..........
; k5 ^% P" Q4 w8 M X - ");5 L$ ]5 }$ w# d: @
-
2 Y7 u/ J1 N9 s - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
5 G* l: I7 y0 i7 h5 Z% P - {1 y$ K; O- U' Q8 P& F
- perror("listen");" {) C6 B+ u; T# u7 l
- return -1;
) ^3 G& d% }" J3 r" d' L% p: U - }" e# h' U7 `8 `) k. s
- printf("listen............# M# M7 k' Q w
- ");+ k. z& f6 W& M, {
- . o8 I& P( L6 x
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
# e$ l5 r. n, ]3 Q - if (connfd < 0)
8 N4 o' m0 X. f, n. ?# L- @ - {
+ B) E x6 M( Y9 u - perror("accept");6 ~5 d! D) Q# @' [+ ]3 [- |
- return -1;
1 R1 S/ Z& g/ a/ _; [9 I# n' n - }8 j: o. w" L) V$ p) S( h$ I1 _
- printf("accept..............0 Z P, {% ` o! P* G9 |9 P( C
- ");
* K+ s0 h. P2 f% E/ ~# a( y, j - char buf[100];//定义一个数组用来存储接收到的数据
b* V6 ^$ Z- O l$ ] - int ret;; E$ h- M, \3 A( L' t
- while (1)
3 Q7 r/ Y3 I; m3 l - {. m8 [/ _/ X2 s+ y3 T; b' _; l
- memset(buf, 0, sizeof(buf));
% d* k5 V8 j7 A# F# Q, L$ P; i - ret = read(connfd, buf, sizeof(buf));
4 P" N3 G/ |* d# U3 a0 f! Z" } - if (0 > ret), s/ V8 R7 A9 C3 L7 a
- {. m# ?/ x/ q& @7 n' [) {
- perror("read");
$ r9 a$ F/ @- P. j0 R - break;6 B0 j' k+ Y1 a* [* v' e- k5 r6 h
- }//执行while循环读取数据,当
/ k3 y, X+ Q9 J/ [ - else if (0 == ret)
: j' _ h9 {( k8 K) b0 ~4 `) Y, m - {
4 X+ q' _$ G2 D; V) n1 [* r }2 x4 ^9 B - printf("write close!0 u& k4 d! I& M$ L! |4 }
- ");
4 v! } J9 P, h4 L" N) T% q - break;. x" R; X- j5 R0 f" c, l0 k
- }1 n3 ~" r* W+ L, T
- printf("recv: ");% J' t8 p% Y+ ]- H) |
- fputs(buf, stdout);//打印接收到的数据3 c( N4 H, O. k: ~# d
- }! i, q, S! s3 h8 L5 C
- close(sockfd);//关闭套接字2 v5 Y+ D# ^- ^6 L0 d
- close(connfd);//断开连接6 i) N, `6 d5 Q
- return 0;
- j3 K* Z+ H- Q; J - }
复制代码 6 R6 D$ K" r% g" s
. C( Z$ {0 u! F0 G) a
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
5 b; L. a' b" E5 g$ C - #include <stdio.h>8 R6 D6 B" W1 n
- #include <string.h>
0 W8 }7 z$ y) F- | - #include <stdlib.h>
. g" D, |/ b* _4 L2 J) `3 N - #include <strings.h>
_ ]" B+ t* I" r+ W% j, l, n G - #include <sys/types.h>4 i4 c1 T' i' C% h3 v/ E
- #include <sys/socket.h>
: b a, { @: p, t# w A - #include <netinet/in.h>
" u$ R4 q' o. J# H$ Y* j( ] - #include <arpa/inet.h>
0 }/ {: S/ q5 }( |% p, o2 O. P$ j) D - int main()* e- v: C6 F/ S7 l/ r
- {, _' T+ {9 j7 t: l/ V" {
- int sockfd;+ V7 e9 `9 E& V
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
) C* M, Y' }5 @3 M - {6 k' k) e, q S2 ~; N: T; [
- perror("socket");! C8 n# u1 ]7 M/ H0 s4 }' M
- return -1;
" a2 r1 m% D- W1 s6 @+ G, A - }" w( V6 m9 q' y+ O
- printf("socket...........7 K4 K5 v1 t* W/ X
- ");
! J6 t- M5 o+ B' r" G1 o4 r8 o - 0 E1 M4 K) x( p: `/ T
- struct sockaddr_in srv_addr;
" h2 e' F9 O/ V; L8 I+ ^ - memset(&srv_addr, 0, sizeof(srv_addr));4 q9 _- x0 K4 G' x& P
- srv_addr.sin_family = AF_INET;
: \+ {1 @$ g2 S2 b9 ? - srv_addr.sin_port = htons(8888);" n+ f: w6 L* B% v' d# k3 z$ ?2 ~
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");$ K. G( c. z' \6 T
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
/ E! A( E7 g/ B% C - {
( V! j" x7 J( |1 `& T - perror("connect");9 }, {- v3 v& u
- return -1; //exit //pthread_exit
1 P7 k+ b- b' ^, y - }
- C& ]$ a+ Y# F N: j6 c7 e - printf("connect..............3 ^' i8 m( _8 V! V( d
- ");
+ E4 {+ C: M. y2 E7 L5 e - char buf[100];
$ M: N3 e2 b: v1 R4 i' S, { - int ret;$ {! P% Z" }7 n+ J" p
- while (1)+ y' X% x) t% D' ~, U
- {
3 e9 k2 C! K! `* h7 J - printf("send: ");
' [7 x6 _7 l- ?- D6 \, ~0 ~ - fgets(buf, sizeof(buf), stdin);/ S" c( I) j3 q7 `" }
- ret = write(sockfd, buf, sizeof(buf));" _! Q D) k8 h! }
- if (ret < 0)4 I( y+ c/ d0 I
- {
+ x6 \0 b$ T- ]3 B: v3 G( Q! V2 M% m - perror("write");
# i9 C; Q6 n7 G, H5 m. z - break;
' Y0 N2 o, A% h7 T& G9 W1 B - }
! W3 e' J% S* V- Z w b- P - if (strncmp(buf, "quit", 4) == 0)9 a5 ^1 n7 O) j/ \$ `
- break;# C4 t( Q V) R
- }: c) a! s2 m1 d' x0 T, j
- close(sockfd);
& z& V8 l, ~2 L0 @: J2 |* U - return 0;4 g! h9 a2 S& `% h7 b' t; E/ |9 z' n
- }
复制代码 ~( y0 f+ |5 q6 V: x- A0 y. H( h
2 v8 E9 `7 h3 j, i
|
|