管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
3 q# ?! B- I$ I' D: o. E# m o& a: d' F, M' P3 l/ X
# e! L, @2 }5 S/ Y5 M* D$ U+ d
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。5 h2 ~, |, x3 X$ I! F; t
: I6 n1 W" o+ @" f
/ @! O( k) y5 _: _8 V z' z9 L$ _
TCP协议
7 x$ @ S0 q, T& D3 c9 {TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。' W% {5 @3 p3 x- D9 ~. d+ H
& C/ w. @5 b$ Y3 L# x1 X
2 F( R$ I, B7 r9 V
关键词:三次握手,可靠,基于字节流。* X! ^, g( l8 s- f! y
- v) _; `( B, s1 r/ m P- P: G5 P2 f: b1 ?, ~6 m" K8 t
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 i7 H6 {2 ~: q. w6 F
3 Y4 l0 d6 F* w- N7 s- Z t& }) t- FTCP服务器端和客户端的运行流程, @8 x+ D4 x+ u' u# S
: S Z+ U% s) V: ?7 Z! B
9 u$ c/ o k9 k( v1 U* B! v
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?& K6 n6 ]* R; _; s: ^: `
; E! `) ~/ n3 N: L) O
3 M/ a0 e1 c8 o9 Z2 w6 L; c1.创建socket
* c }: r# V. W$ B1 m. m socket是一个结构体,被创建在内核中: t6 B5 a2 s+ G- G g- m$ {) E
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议7 N4 s. {- M4 @
) H, I5 _3 w i4 X4 E- J6 Y3 G" Y( K, f/ e$ _
2.调用bind函数
7 u' N2 J0 t' ]" m 将socket和地址(包括ip、port)绑定。
* W* Q8 t4 r: S% R5 o, m' V9 d 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
7 Y5 V+ g$ Z$ ?+ Z+ Y, X5 \1 X struct sockaddr_in myaddr; //地址结构体
, E+ v3 e+ P/ _/ d: W. F R bind函数8 @& x6 t4 F" j" a6 b; a0 `- n
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 B- T8 O$ t6 Q! B9 _) q
3 s. @& R8 K( ^/ O T7 d$ K s) Y3 j- u, q
3.listen监听,将接收到的客户端连接放入队列! l$ \4 @6 |, |
listen(sockfd,8) //第二个参数是队列长度
& a3 T5 B5 R. i. W$ @3 E# k6 X$ P5 G9 E: u
+ N) H. s4 D6 w8 ~$ t) y# d
4.调用accept函数,从队列获取请求,返回socket描 述符4 j! |: }9 k( }0 ^3 ^
如果无请求,将会阻塞,直到获得连接5 @; i3 Y2 h' p0 J8 s/ y
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数8 m$ b5 g4 p( ?& P3 M+ o
0 Y: T+ ]2 y$ Y$ c' A/ \$ l$ W; S/ O: Y7 c5 ^: C& z) k& m: o
5.调用read/write进行双向通信4 Y6 U: U. D d' l9 X! y% t% Y6 a
0 R4 s2 }# R' e9 F0 Y$ y+ X9 N2 q+ C: q6 _+ ~4 s
6.关闭accept返回的socket$ w1 q" j; }% N$ |5 N
close(scokfd);* ~; x4 Y, _' y9 s! Z2 L
* [. ?( K; [) h, V3 y- q
1 b, X; L7 O# k7 B5 {. h) U0 i
2 b0 g5 E( _" g: p* u e/ X% G* X* }% k. ^
# l! ^ c% k* j, M$ G J( W; r下面放出完整代码
" A- S' {+ d, R& v# Q; L
+ s/ T& J7 v6 u" F' h- /*服务器*/4 l& U; _1 e# {% u
- #include <stdio.h>
% l4 `# G9 m& x y T- K - #include <string.h>
7 o! v1 Q1 f: O+ P8 F! |, U) G8 ^ - #include <stdlib.h>7 Y6 P8 T# B* I
- #include <strings.h>
% P3 e" ?: V$ }8 v - #include <sys/types.h>
% O3 J% O) x4 d6 {/ M - #include <sys/socket.h>1 X% S T7 }$ ~2 g; ?! U/ m' a
- #include <arpa/inet.h>& b/ a* V+ O9 ~+ [2 C" \/ \' V6 L
- #include <netinet/in.h>
2 a8 X! z4 O% o- B2 L - int main(). h$ O8 B9 f4 n! E, }4 x, {
- {! Z# a( P& g8 U
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
, T7 c, R+ I, e$ D1 O( u0 j - if (sockfd < 0)
n' l" e! y4 u' X$ \ - {8 u! [2 {3 X$ c9 }* z5 I5 w
- perror("socket");3 c4 m. o9 P- b
- return -1;
1 L/ [" E- V- S4 c) T - } //创建失败的错误处理 B* Y- R7 X) @1 q/ W$ j* v
- printf("socket..............4 X3 k. |" v" [- ^2 F
- "); //成功则打印“socket。。。。”
' e% `+ g6 V( I( `+ a& z+ F j -
& \% i: \8 s3 ~* V/ f7 _1 b - struct sockaddr_in myaddr; //创建“我的地址”结构体9 D- t1 O: n' G$ S
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)9 m- z9 o& K' A* Y% Q
- myaddr.sin_family = AF_INET; //选择IPV4地址类型* z5 z: V/ e2 b: h" H, O8 J, B! s/ K
- myaddr.sin_port = htons(8888); //选择端口号
. ^" @% e: \, s - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
* V) y8 S- `) i' I, b$ p - . N8 P* Z4 v" {) |7 q/ b, Z
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 ?9 A4 z$ q/ Q6 ?/ J( U
- {$ V) s! _6 z# ]. ^
- perror("bind");
8 v* Z @5 d. T# n D - return -1;
% G& R5 D4 n( P( p - }1 Q+ H/ X' \8 o2 k
- printf("bind..........0 }5 P' f7 q2 ~! _/ y, `# ]
- ");
8 o' s i8 m0 G' [" M- Z - % o3 m0 j( U3 d+ I+ O# U
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
; g* A+ j, d0 A4 \ - {
/ y9 V( k* B3 l# h - perror("listen");
- y. g1 P' y; f- B: W$ G% I - return -1;# ]1 m% j9 e) ?3 M$ U7 k! ^
- }: _" N5 h- d; i8 d
- printf("listen............1 p6 ^+ v' b8 l6 D: r G: X0 v
- ");6 ]( X$ W; a; s0 u( B4 {" S
-
2 V( n3 _8 F/ `3 \4 v% A: T - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求 u# k$ N) |' [1 {0 a! S
- if (connfd < 0)( k) p3 |2 F' N3 B! R! Z( {
- {1 S2 D, Y/ Z: M; {( _, E
- perror("accept");: k3 P2 c$ S5 L. P
- return -1; ?* H1 _8 b/ ~+ ]' e9 T+ _2 d
- }
/ v( t7 w3 l7 l' z" ` - printf("accept..............
0 b0 P% i, o5 l1 | - ");
" }( y5 E% @ V - char buf[100];//定义一个数组用来存储接收到的数据6 @1 B5 R5 \( V7 Z$ Y
- int ret;+ e, K/ Z( _( U0 |: a, ?: U8 q1 _
- while (1)
$ |! Y; i; W0 _3 N0 N1 k! I! l - {
9 a Q$ V& ^& U v8 O# b& o - memset(buf, 0, sizeof(buf));
1 ?4 X* G" J* i9 Q: `0 K- J - ret = read(connfd, buf, sizeof(buf));
; I" p$ b" {# ]: \4 e! I6 O/ S: Q - if (0 > ret)
) q$ G" t0 u1 y% s% Y1 E$ o8 a$ a. K - {
2 b2 C5 k) y2 |% c% [ - perror("read");6 ]0 Q" p9 g" S. Q2 X
- break;6 Z5 `: n+ _2 d" R( V( O
- }//执行while循环读取数据,当3 k3 d3 F# u; v# w; w
- else if (0 == ret)* i0 |! b3 D! P, B% r! G( Z
- {
+ G3 z6 [) w9 |- w0 B - printf("write close!
* g& }$ B' N. c: x# @9 C1 n5 g: f. E - ");
9 U3 l. W* K7 I$ H" K" Y - break;. d, p8 @& m F$ L
- }# R, A. J# k6 C3 Q/ G8 S5 u
- printf("recv: ");
$ ~9 g/ N' N) k# K2 F - fputs(buf, stdout);//打印接收到的数据
% X' I) e! X0 Z: t+ r' u6 R - }! Z" K; _* D" [8 F
- close(sockfd);//关闭套接字4 I' X, j- X2 Q* t7 E
- close(connfd);//断开连接/ x& |9 u& J3 e* s( E
- return 0;
/ _5 ~% l, @- B- P: G8 m/ x8 [ - }
复制代码
' ]0 i) q9 I" E: ]- H3 j! k2 z/ p, T: j9 ~
- /*客户端*/(具体功能和服务器一样,所以不再加注释); X) b" ?1 m# i* z' @7 [
- #include <stdio.h>( S1 d; K O+ H: Q
- #include <string.h>
5 _# o) \$ J* o6 x% R8 i7 U: m - #include <stdlib.h>% t8 g3 b0 u* x! A( E
- #include <strings.h>: K8 G2 P0 e( r- ~* r. w, p" ~
- #include <sys/types.h> F# u. J/ A4 Z
- #include <sys/socket.h>
# B5 Y( Y2 J [- {0 x' g( t1 U - #include <netinet/in.h>- J' X# a5 ]2 ~7 a( j8 ^* o
- #include <arpa/inet.h>
0 a) v9 x @7 W0 V" W - int main()8 R2 ^" p' G# X5 V: ~1 q3 K
- {
5 G% V5 ]5 @: {: ?! T5 H - int sockfd;; z6 o2 K' n- l( P4 L0 n1 L
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
5 m) x. a7 {, _0 B* a - {
' c; T" m1 d. T' U - perror("socket");
. o0 N/ e4 I5 ?, m3 ? - return -1;' B( A# q7 c, R, E
- }
' c& [4 p1 d5 D - printf("socket...........
8 h8 K/ R$ K; b - ");: Q% H: G. M2 D6 l
-
& H( r7 _6 Z5 n ?5 U c- D - struct sockaddr_in srv_addr;: i5 t9 u1 K/ d2 {
- memset(&srv_addr, 0, sizeof(srv_addr));" _0 O) n4 @' E' n
- srv_addr.sin_family = AF_INET;
* s: ?+ R2 Q4 z5 O; }6 M4 p/ X - srv_addr.sin_port = htons(8888);
# R5 I$ x; m/ l4 U1 j - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");" Q% y2 v f( s' o; x) `
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))$ d/ L$ E6 X% v8 D% X" ]
- {
, V+ q% |' ~3 x5 t. a" R - perror("connect");9 U. c j6 [+ m! Z8 T
- return -1; //exit //pthread_exit* t& \" Z$ ~: t8 U" V% [& S
- }
5 C* k# d& _4 q- U3 J! { - printf("connect..............8 ~ C+ w( ?: _2 O0 x3 ~
- ");9 t$ k1 S% Q+ J# A; r9 b% ~7 ^4 Z
- char buf[100];
! J3 u5 j3 p8 g0 a' g - int ret;6 A8 w5 C6 J) }8 d t
- while (1)
! }7 B( k5 q5 ^; c - {
3 W* |( T& S; E Q) Z6 y7 |; F - printf("send: ");
. ]5 H$ w! A3 P! T3 [1 @ - fgets(buf, sizeof(buf), stdin);# B% b6 l9 k" t9 X8 w; {
- ret = write(sockfd, buf, sizeof(buf));. k. Y2 [! e$ V& x# g9 z) _4 Q
- if (ret < 0)
0 G/ Z- A: y+ G0 p2 W1 h' M - {
$ c1 t# _( b( x; S. X - perror("write");9 }8 {7 Y' G& Q/ [
- break;
3 a0 i7 e* k# H' d/ d' k - }& t, W" _4 P# U/ b0 R# |) S
- if (strncmp(buf, "quit", 4) == 0)8 q: I4 _% @ D7 E9 d4 x. P7 v
- break;
( D }. p: g- r- t* k' E) h - }
2 C3 i$ ]' D$ Z - close(sockfd);
$ W. o" w: P! V4 H4 J; i/ | - return 0;. a$ j# I: k6 p$ |5 z" f& n& J) m
- }
复制代码 u6 S0 h4 V4 }1 L3 W7 w1 t' ^
6 `7 K# P. n5 ]4 r1 V* A9 l. w |
|