管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
: ?$ b" C0 c* s) d
& T+ G8 _ z+ w' O6 g. G& H% v z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。# B3 k# \; p+ ?0 m% z A1 [/ I
4 K: z3 j6 D; S6 Q4 x
3 q/ ^: W4 c( P/ F" v+ PTCP协议1 F$ L3 H2 J2 w; u
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 }9 H5 D; }# t- r1 Z
4 l- @' o- \( h0 {6 ]( C/ v o" Z& }- i- d# j
关键词:三次握手,可靠,基于字节流。- Z! ]+ w7 }: m; g
3 }* Y5 S1 N8 Q. `/ Q1 x g
* h: ^0 Z& P* A y0 }可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。: |" I9 C+ D& ]8 Q
% ~, D% i6 }$ x6 a" }TCP服务器端和客户端的运行流程
1 q! K4 R: i6 d6 d3 l9 X5 _
: d# m. V3 A9 y# l N& [! s) f* x0 r Q& g, j
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 K% J$ l2 b0 O% ]
1 t2 \1 o: E7 j$ o) g
: l! B( ~, k; t4 f
1.创建socket, ]: `( s1 Y. P( o' `, [
socket是一个结构体,被创建在内核中
h4 t5 i; i, x* p: Z! V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
! H* \6 r* h% l5 ~9 u
) @9 D" r; z- }! j0 s& V4 U- I3 A0 \* s' P7 f& R; \. C
2.调用bind函数
) r3 l0 @4 c# C3 b 将socket和地址(包括ip、port)绑定。( E4 j2 S) w1 K2 L5 n4 }
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
: S5 I( d2 W% o7 x4 c struct sockaddr_in myaddr; //地址结构体
$ a! M' K. D! l/ V bind函数
~+ \6 h# y; I6 Z5 Z0 G+ c5 Y bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
6 s2 h& v6 j* {- N+ C( k: x# Z" ~8 u% @6 n. s5 b7 W: @3 {" A
% B- T! z2 ]6 n; O' z$ y6 e3.listen监听,将接收到的客户端连接放入队列
' W( Z0 c ?8 _ listen(sockfd,8) //第二个参数是队列长度
. q! J: V# ?- k$ ~' g" g
, c0 `4 \+ h$ {' y" r
: {) n$ ^( V' |5 W4.调用accept函数,从队列获取请求,返回socket描 述符
0 x5 |# X- z( a) R7 v ~ 如果无请求,将会阻塞,直到获得连接. |4 j5 ^# N: H$ l6 l6 v! T3 d
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
! y; H* g! j8 ?/ b. N B5 n
* V8 ]3 {. t. [" ^
! q S% E" v3 p& `" j; ^5.调用read/write进行双向通信
1 O9 d, t: O+ S. G$ x, r7 T$ Q( o/ `2 X* G- n9 u' @5 k
2 Z! ?7 G/ L: L8 d2 z4 `8 N
6.关闭accept返回的socket8 J1 u2 k/ s( t) `9 r
close(scokfd);7 _" h6 S6 l8 d$ C# o E
- ]* ~% f" ^* ?7 j; Y( _7 |! ~$ i3 E! H o! V3 l
2 w' n1 k, I1 y, R+ i1 Y; v7 x5 I/ A2 z) M) k
下面放出完整代码
D6 V& ]# ` j/ L
" g6 L" p* W6 D! R; e& r6 m- /*服务器*/
7 w( u2 [" W, V$ P - #include <stdio.h>
" J5 f) f( G5 v* ^6 u+ r; U9 y$ b - #include <string.h>: l) L: W& Q( P3 K
- #include <stdlib.h>6 t7 U- }7 e1 ^2 K1 P6 ^( T
- #include <strings.h>3 J. E, m: W( g" e* |+ O
- #include <sys/types.h>$ U: Y+ U: _+ u9 ~
- #include <sys/socket.h>
! T7 I$ U0 v. s$ u - #include <arpa/inet.h>
5 b3 C& @% Y8 x$ t - #include <netinet/in.h>
9 r! r/ I) f9 f) r1 C s - int main()/ G* {% O: c* ?2 L: {+ M; ]4 k& O
- {
! i. H/ t( N# ] - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
S' s& c9 ? T& D( C; u - if (sockfd < 0)
" f+ f5 W( g) V4 o - {. i8 b* D( o9 O) z% b
- perror("socket");
- ]/ `' l; w1 u - return -1;
# N. q6 ~4 J* M& b o2 R1 O1 L4 K - } //创建失败的错误处理
1 }, \ F* R( _6 T9 } - printf("socket..............% k% ` o' [' s( \# ]! t3 ^! z
- "); //成功则打印“socket。。。。”- a/ }' G# K& t4 b# A+ b
-
, w9 U$ h* N" z/ b# A% _, { - struct sockaddr_in myaddr; //创建“我的地址”结构体1 y: a) q7 E- H) f# P* t5 u
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)8 j2 N, r7 M% y$ w2 Q
- myaddr.sin_family = AF_INET; //选择IPV4地址类型' L: Q. u- p9 k0 E7 X
- myaddr.sin_port = htons(8888); //选择端口号
% r) i+ o; x& k$ R1 q - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
9 Q# Q- R% f- b -
0 @, _4 r- C$ i$ `0 A - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, F$ m# i8 {$ w$ v0 V/ d
- {
) z0 i5 J$ j( H u" k1 w& t1 c - perror("bind"); X* p) @6 K* w0 p; G5 y$ K
- return -1;4 p) [# Z- R- @& H
- }
w& g- [. \9 A! L* |5 \ - printf("bind..........
% ]0 v; B" V7 ? - ");
8 S8 L' I5 }0 V# ?/ T0 h9 ^ - " d# M* k9 D5 h+ u, \5 A( \$ K& k
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
8 y# g" X) `& w2 _4 } - {) a, D& l4 B% Q$ w" Y' _
- perror("listen");8 @3 ~, j1 b. k9 j
- return -1;
: W5 R$ R! R4 H - }3 I6 {1 ~: T" c" K+ }2 p
- printf("listen............8 a: r, H: Q/ ^* }
- ");
; Y: v2 }; ?+ x$ K( F2 v- ^1 T8 F - , q6 V) E, i% [" V) y% P
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求" d) B* Z7 @; [$ D% a b& `4 B
- if (connfd < 0)
. W; Q F7 r; M$ t! R - {" u+ b0 y1 T" `* b6 x
- perror("accept");
8 a( @6 D F6 x3 ` - return -1;
- M7 X: [$ ~, U' P3 ~ - }/ x, E9 x" F8 I
- printf("accept..............3 i4 |% T$ z" L( B
- ");$ R! w) y2 I. h7 X
- char buf[100];//定义一个数组用来存储接收到的数据
' T1 q4 X1 {2 } N* [: I - int ret;
0 A7 _: L# ]! Q' i - while (1)
8 I# ^* ? s3 b: X3 B. D - {
. f. k5 _) F4 i' c - memset(buf, 0, sizeof(buf));! u9 P5 F7 [+ O6 H5 K
- ret = read(connfd, buf, sizeof(buf));
& T4 h7 R5 P7 k( N5 d* u8 l - if (0 > ret)1 g1 J5 t+ K1 k+ p. X% t
- {
' { N$ w% y& O+ G! j8 U7 j - perror("read");4 r. ?* }, C6 w5 S/ f& m
- break;
/ ]" T- N5 l6 t# ~% T& C0 {6 p5 W - }//执行while循环读取数据,当
% C" P |. ?) ]( o3 w4 @8 G& S- m" B - else if (0 == ret)
5 g1 @, |6 o& U3 U/ f! O* \ - {2 W0 j% H) b. V7 t
- printf("write close!3 C& }( Z' T' x9 |
- ");
9 W# ~3 \8 ~ ]- ?* p' e! G2 | - break; C7 ?6 s# C. l6 n9 p$ ]& H/ O; T
- }
) L2 i9 M$ B. u- ~8 b' Q+ Z - printf("recv: ");
- r% |( z; ~) W( }8 ^0 K - fputs(buf, stdout);//打印接收到的数据9 z! I' M' v- ~9 v6 d9 Q
- }
! P( M2 q1 B" C ]. f - close(sockfd);//关闭套接字3 J- p9 i( X# B- \; J9 f6 q
- close(connfd);//断开连接0 ?/ ?$ K1 P2 n6 ~; k6 m
- return 0;
. i* t; V- s" q i: _& g3 ` - }
复制代码
, q& X- X1 y$ i5 @3 I* T; O: O8 J+ G! s
- /*客户端*/(具体功能和服务器一样,所以不再加注释)2 m6 d* o! P2 Y; A- E
- #include <stdio.h>
/ J# W% s( W( x - #include <string.h>
5 k2 Y% z6 N( Z, ] - #include <stdlib.h>- J; u6 ?, _8 c: G
- #include <strings.h>
5 H4 C* ~6 U. ^" k7 c# x( o - #include <sys/types.h>
7 w. b7 _) k, k6 ^& ^ - #include <sys/socket.h>
: t y& [' Q* _% A - #include <netinet/in.h>
$ x/ j2 t0 ~( ~ - #include <arpa/inet.h>
, O0 }- j/ I# y7 A, n% c - int main()
3 a: Y2 u! o: E, J0 q - {& l. k' ]4 ]# X& ]
- int sockfd;$ h2 U/ S& R& v
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))+ d g' E+ g/ H' i
- {; r! S/ f) q: T8 }. L" L
- perror("socket");
: q9 V( [6 M4 ^' |8 ~ y - return -1;# |7 t8 V9 F a9 I, e: T
- }
0 ^# k. H' N# \% F( l; q1 k - printf("socket...........6 \( J9 I2 G1 Y. m! s
- ");0 ~7 Y) D. D$ u- {6 s
-
8 q. Q5 b" V/ i. e& i9 k5 {4 N - struct sockaddr_in srv_addr;
' a3 l# Q) W* {8 Z - memset(&srv_addr, 0, sizeof(srv_addr));
/ ^* e' w4 F$ O* F, A- D - srv_addr.sin_family = AF_INET;
- F; m" F/ [* ]- c6 N: s: C - srv_addr.sin_port = htons(8888);( z$ F1 @6 H) o. f" O
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");0 Y, _5 }! P5 I8 v" k
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
/ w+ B+ x# d& i3 S8 D; g - {
0 @) t2 }! N5 x" A - perror("connect");
0 T) O$ K4 j/ A; p - return -1; //exit //pthread_exit
! ?- p2 @4 p# x' } - }) z- Y. C! p/ n: s1 [% g# X
- printf("connect..............
7 q' q6 o# \3 Z& A$ V - ");
5 Z8 o5 q' [1 U: ~0 M9 d; n - char buf[100];
+ v7 [# p' r2 y* | - int ret;
8 b& @$ V0 r6 z* W; x) U - while (1)
! @5 o" n( C r# g2 ~3 a) U - {. ^9 F, w8 g0 ?- y' P* a) G9 w ?8 ^
- printf("send: ");
! ~0 S: r0 h& Y1 O( t) x9 s. L - fgets(buf, sizeof(buf), stdin);. w0 ^8 }1 }5 N z. j& D+ |
- ret = write(sockfd, buf, sizeof(buf));
' v) M# e$ P. C; J4 O! j - if (ret < 0)
r4 Q6 p1 W/ o+ V/ B/ Q, }* S' f% O - {7 ?' y2 R) R/ h9 I% D
- perror("write");
1 s4 e: S8 z+ L - break;
" J- U! P5 l1 o3 [ - }+ u e! C, j( M+ d
- if (strncmp(buf, "quit", 4) == 0)7 @& K/ N7 O/ K: D B
- break;
2 \+ V+ I" d/ @2 u' \* r - }
4 P4 ^% q, q, y+ K+ p% z C$ | - close(sockfd);
4 }0 D( {) x4 u# f& m S# K9 I - return 0;
& o8 \3 \; z, y) K - }
复制代码 0 r* R$ G4 r' g/ r w! `
; \: Q( a4 C! s: J
|
|