管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。- U! X' b3 K P. B6 u/ M
6 u+ K+ B' \+ C3 T# B! n l8 o$ N) V' p+ y3 ?
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
* l$ c0 B* ?+ R, S f/ w {4 c' ]; N* L% y
* _# \/ y7 F; y6 u. vTCP协议3 \9 [4 K3 p+ f& G' A% k" f
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
, T# n3 X3 U1 h, V j+ G$ X8 p# d& O* C* Y) H
) l- K" z+ ^5 i0 n关键词:三次握手,可靠,基于字节流。
1 a4 A0 ^ ]* }5 p' ]2 _0 S: H0 ~7 T$ }3 `
) T |5 [! b, R& v5 n可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" }0 S9 A1 M' f) j# V9 R0 U* K
8 Z4 v8 o7 T/ k: A/ h) s, ZTCP服务器端和客户端的运行流程( B! U3 v: F6 Q* l5 C7 _
# T2 c, Q4 ^5 J* e# F* U% _' r. }( c5 h, r' h
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
# z0 M8 @' L$ J, N& G0 R h% O
8 m' P# k; e# e$ W/ T% f
# h1 a6 `3 _, `- N1.创建socket
1 O7 W/ L- l2 |; @ socket是一个结构体,被创建在内核中
( v; t# X7 s7 o; u6 D sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议1 U! H* @* R0 ^8 L3 O+ J
, j+ c+ z& o1 u" n& V* D/ O3 X& j
' e* _/ x& n; T2.调用bind函数' Z! t* E0 D! T& n0 h- t O
将socket和地址(包括ip、port)绑定。% v3 O8 H; M1 K! o7 g- t6 c# U+ D7 w
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序* t9 A O: W- M5 I3 V3 b/ F
struct sockaddr_in myaddr; //地址结构体
2 @4 @6 V4 A6 |1 ?8 q4 v bind函数# A: ?8 d# f6 h- B v/ P6 a) B5 n
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))# L2 s I7 x2 X/ t! m, c/ N
0 O5 c5 l0 H# g6 R
3 v! k# C! s$ [- x1 a3.listen监听,将接收到的客户端连接放入队列
0 ~7 ^; m3 J' u0 C: ]6 w: j6 R listen(sockfd,8) //第二个参数是队列长度, b3 Y2 H- n- ]$ v: l4 O/ F4 Z) x
" D6 f& M: j2 j/ {$ Q+ L4 }. g6 c+ x4 y1 a9 w( t
4.调用accept函数,从队列获取请求,返回socket描 述符
0 {; W" N" w2 P 如果无请求,将会阻塞,直到获得连接; h, [- W7 g' x
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数3 \% e. U f( [5 {9 l7 H! ]7 f
* M: e* U- C6 U# F& g8 J2 m* ]! W$ @7 t9 q# |) g) r4 V5 O
5.调用read/write进行双向通信8 U/ b" {" O0 e& W. F0 I
' D4 O- z# q+ r0 _, U! P. ^* `9 H/ t) [5 Y! [
6.关闭accept返回的socket; [4 d4 w9 ?0 `6 h# k5 x% K- N! ~. |
close(scokfd);
; b; W9 K7 [/ J! v; z" f, ]. b# p/ J/ }4 M& H, F
2 t1 O. o& F: C1 j' E
P* A( U' u' R4 u
$ B T# k, D& d# g: ]/ g4 a1 E: m% X! e" Y下面放出完整代码$ Q" N# r$ I. X7 t0 o# @4 B$ c6 B
4 p9 b" Q1 j3 d# s2 T$ s) t- /*服务器*/5 a$ _5 H. F9 g* ?% D5 o4 j
- #include <stdio.h>0 q& D& |! `2 E. y1 D0 G
- #include <string.h>
7 M+ Z8 {, j2 G8 m - #include <stdlib.h>! L; D# `8 b [) R. l# }8 `, S
- #include <strings.h>4 _) _8 e8 \9 N- Q( K5 i
- #include <sys/types.h>
; s/ c5 C7 d6 Q. q* ~1 E& A7 z - #include <sys/socket.h>
" G) l F. r, \" G5 D5 X8 ~ - #include <arpa/inet.h>( j* j0 Y+ p" D* ]
- #include <netinet/in.h>
. E$ A: }9 P3 c% e - int main()
) Q1 D, r5 w! C6 f1 B - {7 d; x2 o/ I) h' V( }
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字6 e# G: F+ z; t1 y
- if (sockfd < 0)
! P( _+ ?" M- F( O- Z. v - {
, b0 e+ k t" Y* h - perror("socket"); H$ E* w$ a2 A- W
- return -1;7 u5 C* L( ? ?+ t$ `; ]) p
- } //创建失败的错误处理! u6 s7 O( k3 _8 ]6 p
- printf("socket..............
6 D$ ~1 {: j1 j- _2 z0 H2 }3 [0 r - "); //成功则打印“socket。。。。”$ {3 a. d! ]2 y- X5 [% I$ \9 H
-
0 B A* D6 g" c9 r8 r - struct sockaddr_in myaddr; //创建“我的地址”结构体
3 m: f- n; b _1 t7 J# E - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见). \6 v: Y; r3 v* q y8 ?
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
6 X: L: g2 q8 u4 b0 e1 E - myaddr.sin_port = htons(8888); //选择端口号
^! ]) y0 P0 @4 e4 w& |8 u! c+ h - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
! W% L' \2 a( e# ^ -
' U+ ~! C3 g" y9 v( L/ b - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 n7 K0 Y! N( f5 F" w4 O" ~
- {
+ F. r9 F& `& K - perror("bind");
& T$ N: j' \% Z2 q+ | - return -1;
) r3 U0 u7 h6 A - }6 [% V+ @0 K' [8 O' A# |' K# e
- printf("bind..........3 G3 @" z8 R" X- `6 z1 R0 `0 y4 f
- ");
2 }/ c7 l4 e9 {$ }3 A - 2 I/ y* y3 I, `8 x# r3 G) b
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听/ C1 W3 P# |) W: p% `. A
- {
7 m, n7 L) Y9 s - perror("listen");* Z& m/ x3 L. o* R) G9 s
- return -1;9 W2 g% o5 d$ l! g9 p# h/ O8 c
- }
, c9 T. T8 e/ k0 F3 R4 F - printf("listen............
& H4 `( h, `% Y& C - ");
. W; ~: m( T( \5 M: @, G1 B -
, o; i* A0 K3 R" e, n: | - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
* b6 j" y3 J( T/ a1 p2 o3 Z - if (connfd < 0)( S1 u5 b6 \- q6 y7 j" _) F: R0 A, H0 x
- {5 P, Y1 h0 h% b0 h0 Q
- perror("accept");0 @ m2 A8 E1 ]) V, V: ~
- return -1;7 l% m ]7 a, C3 k/ d( c& V
- }$ @" O7 a: |- o' f
- printf("accept..............- c* R; }4 I6 T' z
- ");
! [+ d" ~! N7 w& C- r* }9 [! n - char buf[100];//定义一个数组用来存储接收到的数据# B0 E; ^. `! e/ i j7 _! T. P
- int ret;
7 h" B* a; o, l' u5 d7 G, l6 C - while (1), v3 E9 n3 j9 c$ \4 k' T
- {
7 C* ?! f% @$ Q0 Q% j# m' L4 M - memset(buf, 0, sizeof(buf));7 ^; m* k, j2 a+ B
- ret = read(connfd, buf, sizeof(buf));
2 V. }$ T" k# ?$ [ p3 p - if (0 > ret)
9 x7 l q h) N0 N - {# j; H# H! Z) x4 Z6 U
- perror("read");
+ Q7 v( T7 n7 p! ]: J# Y9 J - break;
- R: T( \- s$ | - }//执行while循环读取数据,当- ~9 y8 r' {. T; f, Q
- else if (0 == ret)
' f6 M" h" c; _% R - {7 ~2 I, @4 u6 G
- printf("write close! u k* G9 g: M# J7 \# u6 s
- ");
* h6 a6 U" | b+ \8 c7 | - break;
, B0 q2 v: z6 ]! R7 U* H - }
# ]6 a) x3 S( V2 w - printf("recv: ");
+ e$ s! V' r6 C4 j. C5 L - fputs(buf, stdout);//打印接收到的数据
. `8 T# a* B+ b% s" a1 c/ V - }. T2 `6 r- ]1 u, v# i
- close(sockfd);//关闭套接字9 D8 W8 u2 i* I8 R4 Y
- close(connfd);//断开连接" w4 q2 i6 \7 }% L: J. v
- return 0;
& c( F7 G% Q+ o; _9 F - }
复制代码 , f$ o: g% Y% \8 E* }
* H: H% h: Z) `/ ]- /*客户端*/(具体功能和服务器一样,所以不再加注释)0 Y8 }' A7 h9 |0 b+ ^5 p/ i
- #include <stdio.h>
. x; P* b3 m: | O0 V - #include <string.h>5 `% i' A U7 h8 G
- #include <stdlib.h>% ^" @7 H; I4 @6 H$ c" U1 |# T
- #include <strings.h>
! M+ ~; Q5 Y) R& J4 b5 n2 a - #include <sys/types.h>
2 m7 D$ I! H4 } - #include <sys/socket.h>! N# l1 _* S, i* W$ Z
- #include <netinet/in.h>
6 u8 K: K% P2 |+ d - #include <arpa/inet.h>
. o2 P" G+ b4 s( K' q) o - int main()0 i+ p' p% G) N% V
- {, f% x; Y2 B% K* T2 u& f8 p
- int sockfd;
1 T: K. U1 r, N1 w2 R6 ]7 h - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( w0 ?5 Z4 ^5 D' e# Z+ P" m/ l/ L
- {
& N: P- W: x* g- S* Q# W - perror("socket");( y; x5 W' r7 l2 x" x7 m' L; N" m
- return -1;+ H- O5 ` ?4 c3 ]& a6 n( H8 A8 C7 W
- }
9 G+ o3 s2 I0 ~9 ?/ i* v - printf("socket...........9 Q2 B* Y7 b# u' }" d; [0 G0 N6 j5 N
- ");1 ]# H7 V. N# q+ f) w" a/ o3 k
-
* q9 x, o0 N w- D1 q& ^3 v - struct sockaddr_in srv_addr;" n, |1 \2 o8 s" H0 r3 F. m
- memset(&srv_addr, 0, sizeof(srv_addr));
9 e( C) ?& T* p( p4 a2 ? - srv_addr.sin_family = AF_INET;
6 [$ T' D" y& |8 k# } - srv_addr.sin_port = htons(8888); l2 O2 t5 ~; u' m6 m8 x4 Z. H
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
4 X! w6 a! t5 `, F - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
& H. ]" K7 Z6 |! B: ^# ~ - {: b- p3 `" q' h
- perror("connect");5 w4 s9 O0 l. O" v
- return -1; //exit //pthread_exit
( i0 c# g# \7 o! q6 `' `. G - }
! L7 s5 y9 a( E0 } - printf("connect..............
A7 R2 m/ ^* J# F1 r5 W - ");
, i9 C( A) w& o9 m, x3 ^$ m6 Q - char buf[100];: b* T* q- e3 i: f# }
- int ret;8 l& z( p! t. w1 w7 k) X
- while (1), L' u% d7 B# D1 j
- {
) u d8 o/ \. G - printf("send: ");
% ^/ o2 H: `3 R4 i8 O8 H - fgets(buf, sizeof(buf), stdin);
5 S* l6 P& L, }+ ~! A - ret = write(sockfd, buf, sizeof(buf));6 w: t9 L8 _( I# y" n) Y
- if (ret < 0)3 M( ?% s8 j7 ^& `9 M' w
- {' L0 U# i* X5 x4 s1 k2 y% y
- perror("write");
! p. i" C/ d7 B2 v2 P - break;( q# W' L8 p5 {0 i* T
- }
' {9 d5 W0 F7 a4 ?4 ] - if (strncmp(buf, "quit", 4) == 0)
@6 n; i# c3 Y - break;, k! E2 x. d1 X
- }: P/ \8 t: b, I0 N! n* W& m" v
- close(sockfd);
$ E }; b* i1 B+ a - return 0;+ W, B4 T& V5 t+ u7 ]4 G6 _; ?; Z
- }
复制代码
- m. [+ }; ?1 Q/ T- V" l
% ] l1 `& A. m |
|