管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。& _' {5 s6 }3 q
! t; ?/ Y2 R3 b2 a! \ h5 r/ E. @
0 d" v9 \1 ]1 f B% R0 A' q& \socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' d3 b6 C/ M& d, y5 L: c9 ` b4 v
7 {( d, u" c" f% q! F' ]4 L3 O
0 F3 o$ y3 }3 KTCP协议
& w0 z% `- y) pTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
$ ?+ ~3 g( q# P
! g$ j; P; W: g5 _. E. k d
% s: A$ n7 J. L" T L$ i/ v关键词:三次握手,可靠,基于字节流。
9 Y/ ]* F6 d9 j9 S% o3 a- o
/ j: V- M, ^4 x- T$ y% h7 t" k6 N7 Y. z; y) J+ }
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。/ i T) [( r" Z( g+ @ w
. I: B; _8 u7 r6 f& h
TCP服务器端和客户端的运行流程 S1 X6 h+ J* J L
' ^4 u/ \! w. L& l- g
, K' e% W T7 f如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
) c0 q. F6 Y( y
( I8 E" h6 V) Y' Q2 C( q2 V) ]) Q5 T1 q2 ]$ p# s
1.创建socket# v. }, G9 z3 {
socket是一个结构体,被创建在内核中0 y7 m' L& ]0 L$ F. O
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议( J0 L- ~& ~2 M8 ]2 X
/ L4 H, q5 v* [6 d* Q) |; L4 q
5 j5 k* r! J0 }0 A' V! A
2.调用bind函数
8 G& E' U+ ^ k! d1 _: X! i+ u 将socket和地址(包括ip、port)绑定。
7 Y5 X2 Q- I# J7 u 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序0 B, x* _+ J' Q' h
struct sockaddr_in myaddr; //地址结构体( c5 {5 ]7 a$ |! O3 e
bind函数6 Q4 k5 V# g3 R
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))' Y( Y. @) \# ~
3 Q) \: J! F: x
+ }# f+ t# L0 J+ H3.listen监听,将接收到的客户端连接放入队列& `! `: h3 T# }3 w. a; v
listen(sockfd,8) //第二个参数是队列长度9 n4 Q4 d: r2 \2 s# b( m% N0 r: T
( J9 C$ W3 `: q6 {) F0 {( S/ k W4 l+ K5 @2 ~, m: \
4.调用accept函数,从队列获取请求,返回socket描 述符
& U8 c: v. f/ D) V( f" U" W" a+ [5 G 如果无请求,将会阻塞,直到获得连接
, P- [: J, G( m. x Y5 C5 ~* w int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 [: M0 m! D6 @; H0 T: O- r. B" L( R* o
: Q- f4 O4 E# V& [& N1 m+ }, C
! P Y+ \ ^1 Q! ^9 K; z5.调用read/write进行双向通信
* s/ W% t E t, p- s. g( C( k
0 U8 x, I' ]' V+ i+ @6 C9 h4 Q1 z: F* x8 Z; j( ?. ]& s8 C8 v6 T
6.关闭accept返回的socket
: p7 C& q+ u+ f" `" s, V close(scokfd);9 p- K; \6 g7 b! t
+ @# o+ v- o% I) M3 I
c- |0 L0 o0 Q' q1 {. |8 v: Y0 s' _& |. S4 B8 L
# r; G" ^/ ~, g" b( a下面放出完整代码
; P, D; Y. T0 ?5 f
9 P$ x4 z0 O% C9 n- /*服务器*// C5 C4 z! S9 D) M" a' ^
- #include <stdio.h>" b' v3 N8 ]7 A, G8 o; k! P6 K
- #include <string.h>8 z/ ]+ C" d' J. L- Z( v
- #include <stdlib.h>9 i( G4 o# d- x: x f
- #include <strings.h>
+ r4 L: L8 P7 z( h1 l/ M7 M - #include <sys/types.h>! H+ v0 b" i: V1 E, ~
- #include <sys/socket.h>
6 q) i/ T+ s8 u - #include <arpa/inet.h>
$ U2 @# r. p* ?3 ] - #include <netinet/in.h>
- `! F9 Z& ~8 H( z7 C - int main()
0 R( W' M# {" v3 ~2 D - {+ S& ^4 g6 _6 F( x3 @
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
@) }' v1 W, j+ B4 j7 { - if (sockfd < 0)
& g2 D2 ~0 y9 {: e! C7 R3 U6 J* E$ u5 M - {# g5 d/ m$ _* T5 _" _
- perror("socket");
! r- \3 L; w9 f, u' d& \ - return -1;
: L6 S: U5 ~& o2 ^3 b) `+ \! Z - } //创建失败的错误处理( N# A! m7 L4 }
- printf("socket..............+ Z# j* F/ c, B+ v$ ^
- "); //成功则打印“socket。。。。”3 t; ^$ e1 I' U% H. `) k' D: k
- : |6 c. q0 E* |
- struct sockaddr_in myaddr; //创建“我的地址”结构体
3 M3 i* j+ X9 q; {2 F4 k6 k1 L/ a9 h - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)& \- D, l- H! \7 m$ E- f6 p+ g, G
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
- k* S8 S2 `5 U: I - myaddr.sin_port = htons(8888); //选择端口号+ }5 Y" E' E% R4 S B
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
3 s0 `! \6 t4 i8 d. Y' a -
& W0 @1 B4 ~7 c+ R - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
/ \/ {' l, o# x$ f5 W& x( ]" U, Z - {
/ J( a' y/ X; B! u$ |/ F - perror("bind");
3 a8 Q0 R1 Y* e4 b7 s) y - return -1;( `, k$ k, b) C; K4 P. x
- }
2 M# t8 K$ A f/ ]8 ^! Z* |, i N - printf("bind..........
% t" G4 F* w% {2 R- g - ");
b- T, Q$ Q- L( g A# p$ U - % q4 h& ^, q# j
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听3 ~3 P2 x$ G* P; X+ d3 c+ ^
- {
- _( d. r. w, @+ w6 Y& V. H - perror("listen");7 n+ e" q' s" k$ Q) E. Q' t& A
- return -1;
; v5 r, q! I. m - }
0 z% J# |& x; C5 C - printf("listen............( U' j& a' Y; t9 Y
- ");& T+ s! Q' O3 @$ H6 q e
- 8 V% t$ ~: \! c. D$ {7 C' S
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
5 p. h' j [9 Z% |6 n) v - if (connfd < 0)
7 ] M! U- a. T5 L* w2 C+ m - {
- v9 P, ~. x( @" D/ F - perror("accept");
$ G' F7 ?$ L+ \8 p - return -1;
$ p8 O1 u& y) ^ - }7 @/ e5 Q0 \; v9 _8 D: q9 D+ N
- printf("accept..............& D; ?8 t; y# r# P6 J1 l: Q
- ");
* w# t. \- T( V9 B( l - char buf[100];//定义一个数组用来存储接收到的数据
4 j$ O' E' [; ~7 A - int ret;
( W8 f) U& T. y: I* g, R! v - while (1)
& j* ?7 r1 f' \/ f7 C# n, ` - {
8 k: F7 d/ g, ^6 V - memset(buf, 0, sizeof(buf));
( ?" }+ r6 ~: m8 A - ret = read(connfd, buf, sizeof(buf));
+ h- E( N# |+ \, V- {: u' t - if (0 > ret)0 H9 i+ ]# n" v t) A+ |
- {$ O' U; N& j6 _7 y$ o8 u
- perror("read");
8 U' M8 q! g/ P+ l. ~3 w - break;; k1 r0 O! I3 m
- }//执行while循环读取数据,当5 j! N$ @( U% a+ S- t4 N7 J4 R. }
- else if (0 == ret)' G% |/ P+ g" r% E6 U& q; n
- {' a. |7 e" {3 k/ Z; v; L
- printf("write close!; s) |! ]7 c \: E$ p1 \( q2 J
- ");3 @2 Z) I0 ^! J, z7 L! Y
- break;
+ M8 @6 H: E2 U% [9 T6 F - }
# h! t$ Q' t8 ]6 x- X7 e: J - printf("recv: ");
8 m! y+ `7 Y# R' l, `, P - fputs(buf, stdout);//打印接收到的数据4 x. t$ G5 L7 [; x6 J/ E8 y
- }6 X' A- a% q' c1 R/ U
- close(sockfd);//关闭套接字
- v( D2 ?0 {( D) z9 W; E- _ - close(connfd);//断开连接+ f( U$ h1 @& W7 l% ~* l% D
- return 0;' q) Z( t: h a4 x. o. ~+ ?6 q8 E
- }
复制代码 & }/ J6 i% W& h t: j' D- t0 h4 e$ g$ E
7 e7 Z# w* p2 W9 r$ N% f, v# |( C
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
. H/ ^" M# `3 L) E/ t, l. Y2 O - #include <stdio.h>
- i$ Y5 U5 Z8 G4 [ - #include <string.h>+ Y* h7 R; X) A2 n0 K. R8 C/ C
- #include <stdlib.h> x2 V9 C# W! ?: J6 l: n
- #include <strings.h>
/ E4 b9 {" Y: J) Z - #include <sys/types.h>6 g+ d. [2 y* ^4 @! o4 q" T d; i
- #include <sys/socket.h>+ ? @+ Z+ E* }: @ N' c
- #include <netinet/in.h>2 B, p, M5 J- D I/ Z
- #include <arpa/inet.h>
4 n, Q- ]" d8 ]; b! T9 L4 [ - int main()
+ A9 k z0 _+ @' c' u& [: b - {% M- G7 F: U! e) O) p& h: H/ ]
- int sockfd;
+ X3 [, l) y% B' T2 }+ J - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
8 U1 `8 t" e3 B - {
4 b) Q8 J/ J( x5 b O" F - perror("socket");
9 J8 I. B3 W; C - return -1;
: B: D' g1 M, C' }7 y( l% x1 Q4 h - }5 s3 D2 L8 t8 V& w6 x
- printf("socket...........
- Z( l2 j; T" M6 Y$ T# a' m - ");" O, S, F) J9 _% x( _3 W
- ! J5 i. K( W* w+ x; u
- struct sockaddr_in srv_addr;
( v6 E1 C1 J" K: o" E, O7 k8 J v - memset(&srv_addr, 0, sizeof(srv_addr));) F$ y- T2 ?1 q( B2 M
- srv_addr.sin_family = AF_INET;9 z: Z! h1 T M% ]! {( [" s
- srv_addr.sin_port = htons(8888);
1 `: M; M0 z! H5 |* h - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");6 n: X+ Z0 C6 }6 U9 q( O
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))): y% Y3 v5 Z# e
- {+ r0 G- u- R5 c
- perror("connect"); D6 a+ F# S0 d8 U
- return -1; //exit //pthread_exit
7 q& D% p# `! L9 m$ B - }
, Y9 A2 J/ U. h& K% X' D% J! o - printf("connect..............1 G7 w6 k3 |! R* X/ S% C
- ");
, a9 k5 q- |; v( S+ F; ]9 y - char buf[100];# E/ q' c$ M; b
- int ret;- i( }* y* b; ^5 j" c5 y. h
- while (1)& H6 F! \& p8 S
- {8 |! a# b) s4 R- H0 w: ]
- printf("send: ");
! w' V" c' J$ Z: H+ k+ k( Z - fgets(buf, sizeof(buf), stdin);
. P8 B d. q. h# {' a, C6 u - ret = write(sockfd, buf, sizeof(buf));2 g9 T7 h1 m7 w) k
- if (ret < 0)4 O+ A+ i$ I1 }8 X2 u
- {4 X$ b; x T' f" y% s2 H+ V
- perror("write");7 q; t4 @" }/ D9 R
- break;
5 m; S9 v$ H6 Q - }6 c* |0 h% `6 R4 u5 D
- if (strncmp(buf, "quit", 4) == 0)
# I) V! O( u! D9 T1 `2 e - break;& A1 O& a0 _' y0 {6 w* O- t/ V
- }7 }2 `0 P& C. k3 x6 w% O
- close(sockfd);) w8 c A- e$ y
- return 0;1 n- K0 A* ?( z; M
- }
复制代码
. e9 ~7 V5 X/ z1 E* L3 O( `' |# K' w3 n) h& K
|
|