管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
) U( I5 w; q2 H' h6 {4 J) s) V7 P8 {
+ ]- f* I3 R0 h9 j% l3 b
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
0 a: j& e8 s0 ?" U8 \$ x \& ~3 P6 G8 u2 Z+ J
" R& s# ?. p; j' N1 b" U" Q1 I
TCP协议
# ~' ~) z2 Z/ N; `$ _. S, m" N0 xTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。" D+ p" e3 e% y) Z% y
3 l/ X& G' O$ `' u' [1 c5 Y$ S
9 g# p' d" g* ~, e+ n; ~关键词:三次握手,可靠,基于字节流。' ?' S o" W" f3 t
$ k5 I3 N- s" m3 {" b; z3 W2 @
, j5 ~# ^, x3 C( v" E5 d" `! ]. Q可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
' k+ ?% C% A+ g6 l e8 @
! e8 a! f3 u( T5 k
TCP服务器端和客户端的运行流程
6 M' M2 L4 o. {" `8 c! e# m6 P( X6 k6 ?- e8 P# ?7 s1 n2 a
; @5 z/ d* S. }0 W, z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ _7 Z4 w+ v8 z6 E2 B( K
& M2 Z$ a7 V' ~5 B3 G+ o
, o, W5 g, @( }3 s7 t) g4 N. H1.创建socket
( W/ [* C7 k! E socket是一个结构体,被创建在内核中
w! Z! p9 D: { sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议+ ~" ` A# t: O4 D& B
( g0 O# D' h8 L) d
0 s6 T a/ _/ Y8 E
2.调用bind函数
: ~; J: X+ q, M l+ y [ 将socket和地址(包括ip、port)绑定。
: X& D) q9 T9 C" H9 A 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
- _+ l% j8 s5 K/ G: X* q struct sockaddr_in myaddr; //地址结构体5 ^3 R" t+ F- a; r' X2 }; A4 ~8 L0 B
bind函数
+ a7 v# c* q* l E9 H bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
! a8 b- R' G, p9 P0 C) E8 s I9 K/ M
$ I1 @, b, u0 f' S% T3.listen监听,将接收到的客户端连接放入队列- K R/ k8 l1 m0 D0 h, ]
listen(sockfd,8) //第二个参数是队列长度) p- k% y' d, A
0 X$ l; O8 R! z# {' [
; o7 x' ^2 r# }0 v/ b4.调用accept函数,从队列获取请求,返回socket描 述符
' ?- m% T4 {& J' S" R7 U+ Z% r 如果无请求,将会阻塞,直到获得连接
* n( |$ f. B7 N+ V ^' Q7 Q/ q int fd=accept(sockfd, NULL,NULL);//这边采用默认参数$ ]$ c. z7 f: V! N
4 y9 G: J. k4 Z7 G+ ^5 c- q0 y* G4 k% h8 a. [+ s
5.调用read/write进行双向通信
0 U; A* d$ D# h& H& Q5 z
8 T0 s! r7 }) D/ Q% L+ u G) }- X
7 Y& L; y: ^5 x) p+ j* G) u6.关闭accept返回的socket2 G7 K4 k9 r( p5 z3 Z
close(scokfd);2 X- t9 f1 @) O. H3 }" [2 N3 [
/ u, Z1 b s. h8 ]$ y
! {3 X8 e& |( @; y# {( }$ ]2 H9 \% r3 k; D- _' o/ p* U
4 _: ?- q8 e( ~5 [; W$ A! b: u9 s下面放出完整代码8 Y6 g5 `4 b7 k0 [% R) h
8 u: v% G) `8 X3 B2 V
- /*服务器*/
7 }6 K8 Y! B7 J, d l - #include <stdio.h>7 q6 d) y/ p$ h# o6 j
- #include <string.h>! o H; ^* Z6 T3 \0 d
- #include <stdlib.h>
: n* |) T4 T- S - #include <strings.h>! I* h5 \4 O S) c5 u, ~2 |
- #include <sys/types.h>0 |4 z* U d" X/ Y: ?1 h
- #include <sys/socket.h> z. P6 ]' l0 t
- #include <arpa/inet.h>9 R h* Q/ V4 \! I8 G3 C+ |2 e
- #include <netinet/in.h>
' `# g0 U! W+ n3 U: u |' V - int main()
7 U0 Y+ n, R6 M$ e - {" _4 E7 S/ L; U- d% u
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
. S# l( z; I2 h2 ~9 r - if (sockfd < 0) {- V( P! d# g1 V6 f, L
- {; g( L# |% s' h! L3 a
- perror("socket");
8 J0 \, b3 I; a9 q3 w - return -1;* Z3 Q/ m) `$ \* O: G
- } //创建失败的错误处理
2 j6 W* R# L8 e o) _( @ - printf("socket..............! P0 [+ {0 y! j/ q/ B/ _
- "); //成功则打印“socket。。。。”
- x* S$ o3 f6 V+ S' J$ H -
& A" s+ r: D( A9 H/ a5 ~! } - struct sockaddr_in myaddr; //创建“我的地址”结构体, A& |5 L- N, m. [% M- c+ g& \
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% M5 a: ?4 d- c5 L V
- myaddr.sin_family = AF_INET; //选择IPV4地址类型% @0 E b3 X# L5 ^ y
- myaddr.sin_port = htons(8888); //选择端口号& O0 q: T% x# ]7 s
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址) J& }0 J/ O) m8 o! u
-
, ]6 z; F: M" R - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
7 `& I- Z& }. K0 @- Q+ Y - {9 j5 n( ~0 G @! {
- perror("bind");- E q9 i4 b/ e: b
- return -1;
' h I5 P# n3 Q1 ?& m& h - }
% N& f& b( f8 S, A( n: D5 W, L - printf("bind..........
; w; N6 j) B0 |4 A+ r0 j7 `: N - ");: A" t8 j3 V5 T+ G( b
- / z: K; T5 I3 w" k9 u
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听0 D5 \* A1 n: b6 r) E& s
- {; E2 f& G" R/ B$ U
- perror("listen");, L6 V5 D; z8 `6 J' g( D
- return -1;! Y# \; N% B, v% n/ m9 o# w; j
- }
# X8 A8 C3 v$ `# t7 X' | - printf("listen............9 p! r& N* }5 Q( P) v# J
- ");1 R) R4 Z P# X- y+ \# {
-
8 Z6 w; V0 q; M0 M; s6 \ - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
C, B4 j( P. G! X( z - if (connfd < 0)! o0 q' ?' h2 Y
- {
K: q& ?& Z+ U9 A - perror("accept");0 @- w9 d, A8 M6 e' `
- return -1;
# H9 a: [& Z4 O O - }& W* ^& T1 N, A
- printf("accept..............! O. W( k8 u% A9 Y7 @
- ");# \$ w4 o; v9 h. u5 ~) G. `6 E
- char buf[100];//定义一个数组用来存储接收到的数据1 |3 F/ \# s& N) g# @
- int ret;
! B7 X( J+ p6 b1 Y0 c# K5 X - while (1)4 J: p Z T& \# f. d. M$ c" j
- {
9 R% I1 V+ f6 z( P, W# f - memset(buf, 0, sizeof(buf));5 o0 x% y/ S; Q) `5 V0 r4 E7 G. h
- ret = read(connfd, buf, sizeof(buf));
4 \ q! M& `8 d" D; g - if (0 > ret)9 W* S1 M# O" q
- {
! g1 Q" t7 |9 D9 {* {5 M+ d - perror("read");+ e* K6 T8 m* z! c0 _7 h) {
- break;1 D) f1 J' N: g, Z9 l7 l
- }//执行while循环读取数据,当
$ a9 m0 a5 v" X2 ]! l+ m5 I - else if (0 == ret)
0 l+ a& I: k$ a$ V' ^8 R" v - {, s6 L8 K* V9 C# m( @1 r% B) Z
- printf("write close!) c$ V5 w. S; s1 O- v" @% p4 Z
- ");9 p. G, x9 J5 i# u4 ]
- break;8 q$ j b! g" Q5 w2 c
- }6 I9 S/ _8 [% s7 R5 ?- E
- printf("recv: ");
% q/ g2 [8 j2 r3 d - fputs(buf, stdout);//打印接收到的数据
; T) P1 \$ e2 |8 }4 f - }" Z+ T* x+ @+ r1 }$ W
- close(sockfd);//关闭套接字4 {( C$ u/ t- k! T, C4 g2 m
- close(connfd);//断开连接
8 R/ I O' F5 E/ @ - return 0;
6 b5 ?. W, m( _; ~ - }
复制代码
1 U1 p* b% V) y( H" O+ H3 \
2 s6 B' r* T" @; t- /*客户端*/(具体功能和服务器一样,所以不再加注释)
; ]- B, t, i! d - #include <stdio.h>" H, b- {& X) s5 z
- #include <string.h>% M9 Y! B- N' F: m2 q/ f3 x
- #include <stdlib.h>, r- [! U0 o. L6 u9 e. \) X6 g0 l
- #include <strings.h>
$ j4 R, e8 R: ?$ u6 {& ?/ r: n" | - #include <sys/types.h>
( @4 ]# n6 I3 Z - #include <sys/socket.h>9 l4 E% j$ S' ]* \1 ^0 \5 _
- #include <netinet/in.h>) n) U; \, o. [+ C) }; {
- #include <arpa/inet.h>
0 [5 a9 G) |& d# w - int main()
9 N9 D1 N' q0 n1 m* E/ H- [ - {, c7 u5 O3 u! I7 K9 |* R+ W# Q
- int sockfd;0 k+ h3 {& ]& r6 f" F2 p- J
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
% {- Z$ S! p+ K: H' z- [: W - {
9 K2 X) x7 r) o( D - perror("socket");
9 Q! t/ a2 W% B) l8 s6 \/ m - return -1;+ D4 t4 D. i: |& ~) `+ H$ Q
- }
, { ]4 r5 B5 [ - printf("socket...........
& k1 I( R# I1 t/ d4 P1 W - ");
! O( ~3 V2 i! T2 R: c: z -
2 S# e8 r( O! b& b3 h) g( T - struct sockaddr_in srv_addr;4 C9 T) G" P s6 d/ t' ?4 p
- memset(&srv_addr, 0, sizeof(srv_addr));
3 c; ~* }2 o7 Q9 ~ - srv_addr.sin_family = AF_INET;
5 |) Q7 F% Z1 C5 T8 q; | - srv_addr.sin_port = htons(8888);
) q. \5 }9 n: k: a! V - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");0 u- h& R5 b) x; T Q
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))) E+ K: m6 x8 P2 n3 H0 G- N: J1 t
- {8 Y+ S t/ }7 }+ x* @% Q" Q* V
- perror("connect");. i* R0 \- Y4 B
- return -1; //exit //pthread_exit( h' `1 E R4 ?5 U8 N1 {; G
- }% N1 ~/ i4 L; v" k4 }% a2 {
- printf("connect..............
# V! c; O. X& w: G2 R$ l' u, O - ");, r- h7 O4 U& C. h
- char buf[100];
5 |# W( d( p. Z' X/ ^5 D# d# D - int ret;: h* F0 y9 A1 o1 i. _3 e
- while (1)
3 ~6 V+ O9 k6 o* E - {
) Y# n# w; p6 j& b - printf("send: ");
; h* N2 I$ {: K: c* i* T% S - fgets(buf, sizeof(buf), stdin);
# M& J2 Y T$ }; F3 E - ret = write(sockfd, buf, sizeof(buf));; b0 v: x/ D/ {8 ^% [1 o# J
- if (ret < 0)
; z# X q/ i1 J: Q2 _ - {4 r2 @" m6 Y$ v0 i
- perror("write");
; @$ C) b0 G4 u5 Q6 N" Q - break;
3 S# y, j+ H4 Q3 i$ i$ b$ ]: { - }
- m" Q# w5 S4 \/ o - if (strncmp(buf, "quit", 4) == 0)7 n3 A A& T' ~9 G/ \) h3 h
- break;% C0 x1 V; G | d
- }0 t/ Q- [# v: ~/ t# e
- close(sockfd);
% d# q6 f& G! x2 n+ g* V6 j - return 0;+ w' {$ j6 k9 ~, Z, k9 b/ Q) T
- }
复制代码
" ?) V* `5 V w8 K2 T9 e1 D: O5 X, Z! A9 A& u+ ]" u
|
|