管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。$ `% |" e( f# a) Q
9 p, \' `- ]& D
9 W! i4 d# U! U- L2 e& V( ]9 [socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( U8 x: ?% o4 @# R" I7 k K5 y V, \: R2 v1 e0 E. u
# _" Q+ u6 P5 Z/ |& n( O. G
TCP协议
8 r* O/ u# b) KTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。4 ]0 n+ P9 u8 \, {2 ]( H1 w% g
" z9 ^3 P6 K, X; y0 Y
0 E* T3 f0 o" Q4 f关键词:三次握手,可靠,基于字节流。
( k! ^8 }3 s6 @. W
" l7 b$ b1 a* K3 ?& p1 R8 P: l! ]3 L c" n- @
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。# o P8 ?* T% L- a' j; k7 B
, p8 {) N: H2 U% [3 T/ v
TCP服务器端和客户端的运行流程
( P: A) v9 h8 m- s4 S. t [0 B- e2 |; o. G- x3 K
9 V2 }/ x5 S4 {) v* N9 B
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?; h. F9 z0 P% q/ @
' G/ Z: x4 S3 x9 M5 x3 a* _
) k( v9 `" S' e U! x1.创建socket
" q: M$ o5 B* B- {1 S socket是一个结构体,被创建在内核中
% U) m0 S, o3 E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议$ A3 w. S- K1 K% N1 Z( I
( `* ^( D6 l" I- H |& g6 N
" c) O3 x- B, A6 i3 s6 k: g! {2.调用bind函数
* D9 l2 b6 \* ^7 l8 Z& d* d 将socket和地址(包括ip、port)绑定。
4 z, Z/ v: e8 _ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) x6 e8 G0 h9 p9 @7 S2 }9 ~/ o+ b" e
struct sockaddr_in myaddr; //地址结构体& G: P, N2 l! [" t9 g7 q% A
bind函数
7 Z' v9 X) B1 p3 ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
' z) u8 e- B& p9 Z* k2 a8 j( C* X
/ j4 F/ v6 E4 n5 b/ R
3.listen监听,将接收到的客户端连接放入队列
$ \7 z: i6 t' {; t$ N4 `. R listen(sockfd,8) //第二个参数是队列长度" Y0 `& ^1 C! j2 [
/ L: }$ D+ _! l0 b, h! ?+ f- M5 N. ?
4.调用accept函数,从队列获取请求,返回socket描 述符! M5 w4 h% Z2 ]6 z' \7 z# C
如果无请求,将会阻塞,直到获得连接6 a, o: F: U- F, ]( S% J
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数: V* [* ] Y$ E, }, ~( F! y }" ^7 j
: x7 B5 |* l# V4 t5 R Q% X
! d, ?: p1 x* E, {* p5 B# }: |1 N) y5.调用read/write进行双向通信
9 w, u2 ^' ~/ j, w% |6 U! X7 a4 g- C8 u8 c9 J, z: A, R
5 ^ n; s0 p2 D0 m* r/ O
6.关闭accept返回的socket6 g, h* g9 m) t+ l- J8 ~
close(scokfd);1 ~/ a" A+ W; x0 w# H( J( m
: ]( t R2 D& A: `
- L7 Q2 q4 \2 B7 Z$ h
" I! P$ N5 Y4 R+ C% \! R9 |
! w% G2 E; H9 _$ @( I# O下面放出完整代码
3 n( r4 Q! q& u
( I( z7 @7 H1 K( i& H4 ^: P- /*服务器*/
' e4 k9 m, }" r3 @4 l8 B* Z- X - #include <stdio.h>, i6 l' U1 n1 n$ w6 B! H7 k
- #include <string.h>
X: ^" I4 F& G, q6 E) V) ~ - #include <stdlib.h>
& i3 K+ A1 Y. V8 }/ E - #include <strings.h>- k( M/ b4 A% m& |, I& b+ ?4 a. I# ] M
- #include <sys/types.h>
2 O/ c+ n! S1 I7 y/ O' R - #include <sys/socket.h>
: z" a; @, e9 X - #include <arpa/inet.h>( ^% r$ Z8 I4 @+ i
- #include <netinet/in.h>
5 t- l K6 g) f/ R( o2 O; k - int main()6 c9 L6 p) V" j) ~* Q4 D
- {; `8 y1 Q0 V/ {" Y- C1 ~
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字5 s1 H, z& h. U
- if (sockfd < 0)
: G: q, `( `: J. {! ` - {
C6 e3 L& c, e- T7 O; f. ^ - perror("socket");; y& F4 J+ h7 W6 U7 t
- return -1;
5 G3 Z: Y1 P/ X) t/ A3 D - } //创建失败的错误处理
2 T9 y4 g8 d2 B3 r- O - printf("socket..............
3 u1 W/ k3 B9 _' P, f - "); //成功则打印“socket。。。。”# l, N$ X2 {4 M; P6 W. |
-
4 d R+ e2 L/ n+ K' m Y - struct sockaddr_in myaddr; //创建“我的地址”结构体, h4 Z+ E9 p2 u; q
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)8 g" E) b% M* {6 z. g' E+ m
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
2 h# L# G+ A9 t7 ~ - myaddr.sin_port = htons(8888); //选择端口号
! ^7 d/ Z3 `. c2 n5 T$ [" U( n1 B - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址. c8 R7 ^3 W% v. n% V; j# }
-
) L% M! k! f$ q, M+ n - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 y) o* q* R5 d
- {
9 w( z8 \3 l/ J# _4 v - perror("bind");
( s) r* j' k+ R8 ^! A$ J - return -1;" Y% O, w! F0 X! c7 i
- }
7 I# q& C7 c: ?$ V5 D( S - printf("bind..........: k, l! H9 ~7 B+ E5 p
- ");; q4 q1 f7 ~+ ]' ~8 `
-
8 B( a$ ]+ Y$ c' |! y4 x - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
0 s0 a5 z' s# O* C) o - {9 G" Y6 d8 |1 j, c. J1 G
- perror("listen");2 d4 n; ~1 |, k3 f" l7 R
- return -1;
0 ~4 h3 D( f- L5 q) t; C/ p, A - }) t0 d' [4 [1 ~, e: \
- printf("listen............
) q( L6 h K0 O# S - ");! f/ v: t; M$ `# h' {
-
) D8 R7 N* y7 K! g a/ y5 q; t- }! S8 E# v - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求7 _' ]4 t- o1 v) a7 p
- if (connfd < 0)- k2 _- }0 D: Y( ]
- {: A& b0 O* w% s5 w* z
- perror("accept");
2 ]/ W" R8 y8 l3 o/ k - return -1;8 h8 G) l; _# }# N" ^" g3 t
- }: J, q0 _9 }0 C' [! [& M
- printf("accept..............
2 U) e, Q9 D& O* i! ~ - ");
6 T% p6 r; o- m5 r7 ~# n6 l - char buf[100];//定义一个数组用来存储接收到的数据9 y# O$ P' b1 p2 t4 W W8 ]/ C
- int ret;6 J9 E* G6 t; V, ]6 d
- while (1)
# p1 E" |% }( b" x - {
! A* [6 G! A4 C - memset(buf, 0, sizeof(buf));% x' M% Q) L$ O* q T! X
- ret = read(connfd, buf, sizeof(buf));( \. f" R/ F/ L5 R- V
- if (0 > ret)% z1 E+ d% ~* k5 P0 q
- {" \- {- |& J& x. ^6 W% i# z/ @5 u
- perror("read");" O8 P) W9 U+ e4 ^/ J! j
- break;! f+ J9 o, Y, l) U
- }//执行while循环读取数据,当
6 X. \! _0 U7 y. b5 k1 p - else if (0 == ret)
7 y' W! v4 `) I0 N$ F/ a$ n - {$ _% t0 {# P: P" V3 S+ z
- printf("write close!
. q' `7 U0 d) Q2 w$ V. q: y - ");
/ z2 _$ D1 t3 e. P5 _+ K9 N" i - break;
" N* w& P4 @! L3 [ - }" _2 U t* H; z$ v& P6 a7 c
- printf("recv: ");' z- o5 @3 m1 E: b; V+ u! L
- fputs(buf, stdout);//打印接收到的数据+ b' V- c$ ?2 D8 j$ b
- }+ z, o& e- z9 Z, ]# o) i
- close(sockfd);//关闭套接字
5 Y2 @: N) C% G2 u1 H# S - close(connfd);//断开连接% e5 _1 Y a& s) I4 ~; E4 k
- return 0;; a0 S U1 P- s3 A% w
- }
复制代码 3 P/ u3 \0 b- r3 ^- Q V; ?( L- w
4 r8 W2 M Y5 {! n {- /*客户端*/(具体功能和服务器一样,所以不再加注释), B# b, g; ?- K. k: x
- #include <stdio.h>' P- o5 O9 I( o0 p+ G
- #include <string.h>( n0 L& [5 p( @ E6 w4 Y
- #include <stdlib.h>& g# W( V( J5 f* A" [3 F
- #include <strings.h>
0 D6 |! N9 E) `( s% Z3 m - #include <sys/types.h>
* t+ T+ c2 l& E: B- m - #include <sys/socket.h>8 a$ M. v" w% C0 s$ d2 ^) h
- #include <netinet/in.h>
5 z' f3 q! Y" H+ g - #include <arpa/inet.h># f3 F, y/ y A0 r% `: }! t
- int main()3 x2 l/ a: \1 c$ R
- {8 c8 k. C0 A/ I( ]) D/ `) D
- int sockfd;
. M \, b% E% V- e$ I5 S - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
4 E" y/ Z# }) x3 w, o - {6 w' z- _: `& Y. }" s9 v
- perror("socket");! H# Z0 b* q) A. q2 E4 b$ m
- return -1;
6 w' f, A Z E6 _2 k - }
$ b4 u7 W+ S, y7 ~" j8 _ - printf("socket........... s/ U" E$ Q' `8 h" y% t- W/ L
- ");
8 U* k c0 D1 y; X - $ k6 T1 @ p" s% y! d. `2 }9 q
- struct sockaddr_in srv_addr;! \& A- C1 g. A' R. w& w
- memset(&srv_addr, 0, sizeof(srv_addr));
3 s" D0 a! J2 f - srv_addr.sin_family = AF_INET;
: y1 |7 ?1 c# @3 I; \ - srv_addr.sin_port = htons(8888);. r' n1 r; e5 i3 ~( j* z* H( q
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
5 n _' A& m0 N9 z% P9 E: M0 y - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
, @1 a6 [7 }4 f$ I% W - {
7 w; @+ N2 z- q4 [/ u - perror("connect");
" L: U1 E- r6 X" a, _$ m6 ` - return -1; //exit //pthread_exit$ J6 M% G, m, a" [- F
- }
% x; G2 {) M+ o9 S" Y - printf("connect..............
* Q- C3 i4 G. R' i2 V# p - ");1 y% ~5 n/ s) b
- char buf[100];1 p/ g/ y" z& ?& q
- int ret;; Q: {; R3 Y) X Y
- while (1)
1 m; r& b w: M6 |2 f - {
`0 y! A0 p+ c - printf("send: ");( F2 ^% e# @# S2 [9 [
- fgets(buf, sizeof(buf), stdin);
7 E8 [1 c, y8 _: B3 b8 F/ Q - ret = write(sockfd, buf, sizeof(buf));+ r1 h+ k) \7 Q; o9 ?& Z( C+ G
- if (ret < 0)" B: g& M( g, {! |$ G" Z! y
- {; C9 r" M& c" |' A1 U/ Y& F5 E r
- perror("write");- | l1 R) f: m
- break;
1 R4 r' ^+ U) o - }6 s( B+ [4 [% \0 { H
- if (strncmp(buf, "quit", 4) == 0)2 C3 j7 l; N7 i' S. {
- break;6 Y* D) A6 _, v! X! e; T
- }
- C* K2 k" h: p5 W5 ?; ] - close(sockfd);0 u4 n- V+ @2 j5 I3 _
- return 0;
$ D0 O9 ]- K1 E' G1 E - }
复制代码 + |, S7 |1 D( a- u
' x: k+ h( O- o6 n7 o W9 u8 m5 Q& U |
|