管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。! f; p6 s2 X% x, w. n
4 z% w/ f- H% l$ b# k6 o+ n! l+ f
6 r& `- P* X% S5 lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
$ f0 F" ]4 d8 m% C
* }( Z" _" i0 z4 \+ E3 C% y" m9 n# y+ T; E, P6 i
TCP协议
! v) C8 I7 S1 DTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。 n( R4 s ^8 J7 D( v- ]1 Y7 Q
. J$ ]( G ^& t
6 r( V3 H5 M$ k- t% J关键词:三次握手,可靠,基于字节流。
* ^0 ?6 E2 U, t$ h$ z: C, P5 E. ?% U
) }6 n d4 ~9 L k* ]4 @" t可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。3 F3 Q0 k8 ]# M
; }8 I$ R2 J* mTCP服务器端和客户端的运行流程
3 u# |) S1 s, D7 ~ ~
5 \$ {3 N' _, _+ _9 c
1 {9 y# ^9 R3 L如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ [) ?5 m- I" u" |' M
! [: l: B! g$ m* o; l/ ?8 T
! x: m' \9 z/ C. k6 X* B# e1.创建socket
) [1 ]( ^8 _" ~! ?; G6 f socket是一个结构体,被创建在内核中# m7 }& J9 U, Q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
1 }# Y4 S# |" m% e1 h7 K! B/ D1 p# `. _0 X, t( o" ]' U. C! n
, Z" M$ s G; M; i& a2.调用bind函数
$ V$ m1 h4 ~0 `2 ?" ~3 a 将socket和地址(包括ip、port)绑定。
: p! Q4 K9 F; t' m& S) ^2 x 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
2 L. [8 p! K. ?6 e7 _ struct sockaddr_in myaddr; //地址结构体
8 V' A( b! Y$ q G# L! d bind函数, `& `1 m- C. D. \ T& u
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 E' a$ i8 T4 ~9 q. G! F3 S4 c" k1 F, b$ N( s
3 O1 R. x$ }3 A7 z6 C6 l9 l3.listen监听,将接收到的客户端连接放入队列8 X: U1 ~, k1 `7 x! ?4 g% r! O0 f$ @; D
listen(sockfd,8) //第二个参数是队列长度
" b( J Z, _: p- \( F; k. E: K1 P z# ?: K8 y- k
& t3 t' @* `( D& @4 u7 `' r4.调用accept函数,从队列获取请求,返回socket描 述符# x1 u6 U+ K; U
如果无请求,将会阻塞,直到获得连接
# j0 L; a. s/ [) P int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 p' h ~) i0 Q6 e- M
3 O% ^+ ]$ i$ w# ~6 M4 F0 }
$ B% t' c+ P- \+ j, `
5.调用read/write进行双向通信
: N' [) A1 R# b5 v$ I# O' Z1 ` M# \/ q
7 I1 h4 P; g* ^& x' L1 z G6.关闭accept返回的socket5 Y8 i+ J( |; A
close(scokfd);
! e) h& J% t1 r% Y* [; g1 s5 @. g1 X1 G0 }" g
7 l; @7 L' s ^+ f# K" v' A6 }
' \% j! t' G) f3 s# c# z9 ^
% D" p9 g( o7 E- ^: y下面放出完整代码
& ~; K2 L+ U7 e' p* V: q; l: p
% V+ F+ V& }, U, I" F- /*服务器*/
* I+ l& P3 V9 k1 w5 e' H - #include <stdio.h>
5 L8 c5 p! t* ]( F1 W- `: z - #include <string.h>
" t; Z+ e! M$ s1 j5 L% } }& d' b* @ - #include <stdlib.h>
5 X5 q! S" }& o* V& @ - #include <strings.h>
+ r5 m9 x6 U) J, a# J& K, j. ~ - #include <sys/types.h>+ \+ a6 Y6 G. }0 |! q, T) A+ T) a
- #include <sys/socket.h>5 ^6 G6 X! O) | x- e
- #include <arpa/inet.h>
8 q2 U' n. f( J - #include <netinet/in.h>1 U4 B0 |& F) K* g! e3 v4 |; f
- int main()5 b$ ^$ b! ? R M; M0 P# `8 u
- {
- I0 ~( Q; {8 k$ {6 x9 D - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 O1 T6 F, c4 T, P P! a
- if (sockfd < 0)+ [2 Q0 ^% D# b! R% T
- {* U5 t6 q% @$ K0 o1 ]% e" n" S. @
- perror("socket");; v1 [! }& r+ V* c( J# T
- return -1;) P% |1 B+ d4 o2 v s
- } //创建失败的错误处理
* J$ t/ s- y _* l/ O - printf("socket..............
0 l0 h- \/ o" y& X - "); //成功则打印“socket。。。。”7 ?5 V9 V7 }. C2 \2 v- w- v. |
-
( |7 J9 o h5 { - struct sockaddr_in myaddr; //创建“我的地址”结构体
3 ~- Z* c: ]9 S6 G& Z' W' y - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)" C: M4 X2 Q5 o
- myaddr.sin_family = AF_INET; //选择IPV4地址类型& A, w( h+ F# p7 z$ R( N
- myaddr.sin_port = htons(8888); //选择端口号
4 G) ?( u& B( F* S& J! R - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址* x5 u8 f8 {7 S- m1 ?
- 4 v8 m+ N5 Y: s: Z/ P, {8 ?
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字/ V/ @, w: ?: h( W7 h
- {
: T& @2 R3 b$ v5 K4 x- p( [+ K - perror("bind");( i5 h7 K, M% V2 _
- return -1;
: B& B( r7 K& \5 D: L- M& h+ e - }; p$ a' n# O$ ^: j
- printf("bind..........
5 F; ~! x3 Y( j2 R; E! e) X' s3 g" y; f - ");4 i' k) b- }% ^, ~: K' P% s" Q6 _
- 3 l4 J0 d# O% @0 |9 C9 k9 T
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
% m, Y6 N) F F7 U& c$ f - {" _) F4 [ _. E! H- z
- perror("listen");
$ x1 p2 j. k& Q2 f. Y - return -1;
$ J' {& H5 t) b& L5 P$ }3 a" v - }: X+ |5 o2 I7 E% R6 I! o
- printf("listen............
% \( N) t2 i. k$ K+ J' T - ");
- O1 _( C2 \- t% V- {5 J - $ O$ Z( a, u( o9 m# @: \2 X
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求7 D7 {2 }; a5 e
- if (connfd < 0)
+ a a- f3 u z4 X% a* P - {* n% }' Z" d' [, b" p1 C: g V
- perror("accept");
9 [9 W6 d9 T& f7 g, U& s - return -1;; s7 x9 T8 M' E
- }
9 |- z" N0 j$ K# }2 J - printf("accept..............; X4 n; I" j; @
- ");
9 B6 m! b+ j4 L" N& l5 i - char buf[100];//定义一个数组用来存储接收到的数据
9 D9 q7 F, e, G M9 | - int ret;6 P' s' O: v5 |, P8 m
- while (1)
; `( r6 d5 `, Y) r4 O, h6 C - {9 q3 X' [4 U2 v/ h! T! u
- memset(buf, 0, sizeof(buf));. j) K3 w! C- i+ Z# J& V" }. W' ~6 r' o. ^
- ret = read(connfd, buf, sizeof(buf));
# e% G" F# v5 ^. U3 ?- q' D - if (0 > ret)
+ x* O: e0 h! T1 ~ - {
6 Q; C# i5 a' B1 r - perror("read");% b- F8 n5 [$ z, s9 U2 K
- break;" J9 ~2 O0 J2 y! u; I
- }//执行while循环读取数据,当
3 s9 ?3 w& j7 O. ?5 v - else if (0 == ret)
4 O9 N/ w, G$ x9 @9 j. v/ ^ - {
; t, F7 k, A, U - printf("write close!
1 G; X7 f+ q5 b# `, ~ - ");
% n! A H4 K( v+ k G - break;
: d% I2 x7 a& h$ b5 O - } P, e2 C8 G' W6 J
- printf("recv: ");
# n: i `4 N& f. ~% Y" f7 T1 D8 g - fputs(buf, stdout);//打印接收到的数据
( H6 [) I1 e" V% i - }& e& i$ n# i& n; K0 Z
- close(sockfd);//关闭套接字
+ w6 E- E4 k" W8 Z9 R- K& x+ ?4 J - close(connfd);//断开连接
# h( q, E% x+ `- {1 b z - return 0;4 W" l% L$ l& g5 d, m% W2 v# Z' c) Z
- }
复制代码 " s) {+ L# T- ~! h9 @2 X; M* b
* X* G1 v# r* A5 e9 V% c
- /*客户端*/(具体功能和服务器一样,所以不再加注释)$ H7 a* b4 r6 c7 u
- #include <stdio.h>- E) A0 `8 Q& X9 f( l/ W9 W
- #include <string.h>' [# v. i. T4 w% A# n$ J
- #include <stdlib.h>7 Q% H* r+ o2 E" G: c: d- `: }
- #include <strings.h>7 q/ V+ ^+ O: e5 {/ g
- #include <sys/types.h>! `: H# v2 I: D5 A( C$ `' i1 z' q$ o# x
- #include <sys/socket.h>
+ `+ C$ N, {. d9 h7 |) ^# m - #include <netinet/in.h>6 ]+ e7 }6 Q Z4 W0 f* z0 m2 p- O1 Z7 c
- #include <arpa/inet.h>
+ |8 L$ N; @0 Y2 W1 Z) R - int main()( [8 I7 v g) p u' n; I
- {+ j; c; Z N/ Z& }8 E
- int sockfd;, Y$ L! @/ b5 H9 l; u$ ]; o. L
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' h5 U- ~& J( N' f8 o, k( ]1 @
- {( H8 I& r' V' Y) [5 G( i
- perror("socket");
) [: A; U3 b% o& I - return -1;
; |6 }; v. [: E - }
0 f+ d% H& \. o3 _- c - printf("socket...........
g. p1 y8 [0 V$ J) ~8 Z9 O' p# R: i - ");1 m$ N5 \8 j3 [6 X5 Z
- ; \# O& x+ w+ j5 l+ y
- struct sockaddr_in srv_addr;& b0 N5 l' f# h
- memset(&srv_addr, 0, sizeof(srv_addr));
$ C% P( I5 ]* d9 o - srv_addr.sin_family = AF_INET;) G/ D h U3 n4 k5 `
- srv_addr.sin_port = htons(8888); s) e2 S4 P$ T4 ^) u/ e, R
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
% j |6 B% K' q n6 V9 F) k7 s - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
) Q/ {7 J( K. V2 p, r/ q% S - {; O6 ?* e- @7 C, V9 M' y; H$ Y& f
- perror("connect");) K4 N7 Q: @) M; f
- return -1; //exit //pthread_exit6 L& |/ L X9 e/ y
- }" b! y0 x+ e' d% f: ~
- printf("connect..............' f/ n1 \8 G5 m* Y, t
- ");+ T; r* D. Q7 c& z6 i. Y4 u* a
- char buf[100];$ k4 Y1 s1 }3 f( E6 `* X, l3 u
- int ret;' W3 |6 O" k2 L5 ^
- while (1)
1 `+ C0 Y& L) X$ N) l) y - {% v6 Y! D6 G* w [- n
- printf("send: ");
0 o$ y* J+ B9 Z* j& L% i - fgets(buf, sizeof(buf), stdin);
) Y$ Y& @6 G7 f0 d0 C5 A - ret = write(sockfd, buf, sizeof(buf));' _/ Q2 Q( p6 H: ~& D$ x' h$ Y
- if (ret < 0)
4 S/ [6 D7 u6 s& F- v - {
3 C0 Q0 m+ y0 e7 w2 q8 U! g - perror("write");
! `! _" }$ H/ u4 g - break;! O$ [/ Z3 d( N5 T4 G9 A% P, K% b
- }
6 X8 u- Q9 s l- ^ - if (strncmp(buf, "quit", 4) == 0)
! ~: {0 x; }3 X' u - break; x# R1 ?& \! i8 H
- }
7 F s: N! Q+ J - close(sockfd);
3 R$ C- G# o( z! I8 b - return 0;5 b1 M9 o* e/ U+ h
- }
复制代码
6 U% B, }% _. h9 n, @& E' k' Y
" u8 P1 b9 D! j7 N6 }( u |
|