管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。4 i! N. Y% Y. u7 v! B
7 y6 V# f6 H/ ?; ^ ^% R0 D3 L0 S% B: p% s8 O, r
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
, K1 o) z+ e% k' U3 k q0 \5 F% P3 I" c) `* s( E! X& u2 L
8 L& J$ {7 Y$ o3 y8 d
TCP协议
5 @% g M0 o) a0 }TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。: x5 u' D- T/ |& J6 x& W( n
+ \6 P: f5 E+ _/ _2 W9 t7 u. M$ S6 v1 c2 I
关键词:三次握手,可靠,基于字节流。
: F- l6 F3 v( o# J2 N# R0 }& ? g' L# _
/ a6 a) ~0 G. S6 L) m. b; {可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
/ f: |) B* e4 { T3 h+ ~ w
+ {# x8 b/ K9 GTCP服务器端和客户端的运行流程8 t) _) `8 P u$ g3 s; ?/ d
! u4 i" f* ?4 e$ B1 Y
6 g: w D" U# n8 `: Z. R
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 O; k. X* H+ ?. B
b1 z6 d+ p$ Z% ^4 L+ ]$ U3 `0 f4 w7 D) e9 S% ?) }
1.创建socket+ ^6 t5 \# G3 v) u" Z- O
socket是一个结构体,被创建在内核中
2 \4 |. z; \7 I+ Q sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
7 q% X4 u; ^* I* _
; x4 b. s2 X1 j% s) A
; Z9 v' z# [4 y" u; w4 q2.调用bind函数
- x, Z# C" y; o8 K0 P+ U 将socket和地址(包括ip、port)绑定。
$ Z& t# C6 b; K, s, \ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
8 i9 {% `- b4 S# [: C: f Z- r struct sockaddr_in myaddr; //地址结构体/ Y% J8 b: X x# {8 e" j
bind函数
2 q7 D/ _* Q/ q C bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))" N, Q o* H0 L0 `: [& ?- s
6 x; P2 X1 x: ?1 c9 l
1 x) a& I9 x G' M- Z3 w" Y3.listen监听,将接收到的客户端连接放入队列: t+ A7 r1 ^4 \" }5 }
listen(sockfd,8) //第二个参数是队列长度, D1 J8 E( |- S/ g% E4 D4 r
4 R2 o1 B7 D9 G" q2 t: S6 M/ i) q$ G/ q; G0 ^7 [0 k
4.调用accept函数,从队列获取请求,返回socket描 述符
9 f/ {0 o$ L- M 如果无请求,将会阻塞,直到获得连接
K' @7 o- m' k) e8 y int fd=accept(sockfd, NULL,NULL);//这边采用默认参数! t, O2 l& I s; U+ ~. o# U
# X2 i; ~ O% k8 G0 i, j: G# y5 ]6 R; k6 f- _4 \, M$ |2 e1 m
5.调用read/write进行双向通信
) o+ Z4 Y! k% R( `+ t& d2 y
7 v* `; W }& |" z5 s3 c) k2 d! B' Q. ^8 j1 C
6.关闭accept返回的socket
. ~5 D, ^: G$ n `- W close(scokfd);
: j& T/ H6 W5 p( r; q p
. [% g6 `. ~. K1 ?$ r% h8 U0 ^) R' I8 _6 P( L) r4 w! R9 e
' Z0 X7 n4 O9 t. I
5 W& S4 R9 W/ y$ j: G8 b, o$ p9 F下面放出完整代码
* M; U. n3 V; }8 s: F5 L: l
3 E; ]1 G# b! @0 M- N- /*服务器*/
) {) U6 G% D# f, @- P1 ~1 l - #include <stdio.h>
4 a$ M! t$ w# D6 J! V2 U% O - #include <string.h>
. T3 c1 |3 [( p$ d - #include <stdlib.h>
, |: D8 k0 n1 N$ j" H% f6 o% ~9 O - #include <strings.h>
/ `% t: l5 i4 R7 v, c2 m - #include <sys/types.h>
9 b0 Q' G) y: h; ?5 X - #include <sys/socket.h>% t. }# ^% D$ v& F' o! p7 H! X
- #include <arpa/inet.h>
+ K" G& I _9 p0 \7 p$ X" g# {1 b - #include <netinet/in.h>9 H' c( T! @7 S4 \+ `$ U
- int main()4 ?- c# `. J" f
- {" Y; s9 a) J. }( A
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
) e2 ^# o# ?. f0 J! U - if (sockfd < 0), v: E. h) w+ U. T6 N
- {
2 m; m# W' j: G# P1 p6 C) ^. V - perror("socket");
1 ~) T$ `* C: d0 E% [! m - return -1;
/ }( V4 [& r7 S: U; S9 c - } //创建失败的错误处理
' j- o9 r: m, ~3 v7 v - printf("socket..............
9 T4 u7 y) c9 r n3 U V - "); //成功则打印“socket。。。。”
E1 D ]" Z6 [# ?8 F& a* e6 a -
% W0 T5 A, {3 x% a8 q& O# {9 X - struct sockaddr_in myaddr; //创建“我的地址”结构体
5 v( a$ H# E* H* d! @" h6 g! N& U - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
& D' B/ K4 y- o A/ _7 \$ r - myaddr.sin_family = AF_INET; //选择IPV4地址类型* w. P3 @- D1 k7 s/ N
- myaddr.sin_port = htons(8888); //选择端口号
' J$ d5 o A8 d9 E - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
6 S( Q5 X/ l v: A -
! I* W& }" J4 K% I* O, U - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
' A4 U* v( @- P0 A% n0 {& I - {) u! O$ Z) A* {4 j/ z' [
- perror("bind");
" a1 m, Y: E: i+ ^6 ^1 Z0 N! ~# a' k - return -1;: y9 z) }9 W; ?
- }0 q1 a& a$ e/ z9 s( _9 F
- printf("bind..........
" ^8 S! [# k9 a f8 p1 x - ");
7 K4 u) r- W! C s( k2 s - 2 B. v8 M6 x! ^, {, i6 c* F& F) F. Q
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听# h. M& Y" ~% o$ Z
- { m/ e) c& Z% d% V3 _
- perror("listen");
3 {+ T7 w1 h2 ~4 j c4 [, z - return -1;
( D3 x+ T% Y0 G2 A e$ L# V6 u - }! O& n) O( E& @1 x. d2 y
- printf("listen............8 V6 T9 z. M: e) J9 e
- ");
+ Z4 u1 z. G8 Z: c" ]1 `- q) {5 a0 c - % j1 e2 a) u F/ {! Q+ U4 h* R
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
- N- n, Q# u: n! @8 J - if (connfd < 0)+ |# q4 N. s! Y. }1 B6 T j7 ?, [& ~
- {
# O0 @4 J7 J3 Q. V6 B; E - perror("accept");
1 D) e+ B% | M }% k( W! L% d+ y8 @ - return -1;
0 @. T/ o1 |1 ?3 O3 A2 q - }6 W8 |9 a, _) ]0 r, K; s
- printf("accept..............
: P. z+ V) z& a! s9 Y - ");, D3 Y) n2 F+ K6 `% e
- char buf[100];//定义一个数组用来存储接收到的数据
& o( d* t# }0 G. W4 o [, y - int ret;$ v. @: g* l) {- U" Z
- while (1)- o9 ~; g( A5 m$ I4 Q; [5 X6 @
- {
! [) j; N4 S5 y! M0 l) f3 j - memset(buf, 0, sizeof(buf));
/ @3 N$ l2 H# B. {. ?! m% x8 `; K+ D - ret = read(connfd, buf, sizeof(buf));
i3 n0 b: r! C7 @ - if (0 > ret)
8 k7 b. h; h8 P# f - {8 ?7 X$ B8 Z( P0 L0 k; k! R! c' Z
- perror("read");- o2 x9 ?0 G% v8 m
- break;
" ^' i/ y5 `* J8 x1 u - }//执行while循环读取数据,当
/ \' ~) @ H4 ~4 `( c# V4 J - else if (0 == ret). j7 v1 Q: y9 n7 k$ ?' E
- {
. I' u$ ]+ e2 x6 Y4 g+ L' R - printf("write close!
# I: W# T0 c/ h1 X' q4 x: w' y - ");
. R$ h% u, _$ ^& i- s: @, z - break;3 F) M6 p6 J5 y6 J. H5 i
- }- X9 t4 N: Q, O% T5 \
- printf("recv: ");
. Y) |+ H+ I0 e0 }8 P5 h - fputs(buf, stdout);//打印接收到的数据8 H5 Y. V8 s) F' T# R7 B
- }
& `8 J- m, c- l4 v+ l' e - close(sockfd);//关闭套接字* D6 c" }6 }) R1 g; S- _: u2 K; h
- close(connfd);//断开连接
) s% |& l: F( u0 b - return 0;
" z6 g, O( j2 B4 t - }
复制代码
: [! a* Q) J3 m$ S* a! `" @7 W; Z" L" J5 ^. G6 L7 s. g, [
- /*客户端*/(具体功能和服务器一样,所以不再加注释)$ K0 A9 d. S. ^4 V5 y v J2 {
- #include <stdio.h>
0 A) }8 l- i& a - #include <string.h>' t3 s8 @5 @+ m5 h* j* w' Y
- #include <stdlib.h>! a( P( ~! s6 O
- #include <strings.h>
$ e/ o. F* m& l) i" n& f1 { - #include <sys/types.h>. O0 E& a* Z3 v3 Z9 p: E3 D4 V
- #include <sys/socket.h>4 p0 [/ u% X4 F/ R+ N
- #include <netinet/in.h>
$ y( O* v4 D6 k! _8 h, m5 v - #include <arpa/inet.h>' A( \& O1 J" D! J
- int main()
6 P' W* N+ d1 V r, R' Z7 Y% @5 O - {
$ E& {2 w f) i& c+ A7 J - int sockfd;5 ^5 _: D5 a' y
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))* M) q& u6 X/ `. D- ]/ v% Z9 h
- {
7 k; ? h, J; e& y) B5 E$ J - perror("socket");: A5 D; @" A7 A
- return -1;6 @$ T8 Q6 y4 Z: P# b! F
- }9 Z7 t5 J: i, B/ C0 @; J/ G3 L
- printf("socket...........) P7 u; M) }7 u, W$ q4 o
- ");! H& R$ W* A8 {: w% @6 x
-
5 @' @; v6 [" p+ M3 O7 k - struct sockaddr_in srv_addr;
$ K# _% l# c5 N7 Y R c - memset(&srv_addr, 0, sizeof(srv_addr));# p3 O0 z5 \1 B1 e' v
- srv_addr.sin_family = AF_INET;
+ M9 ~# p# R% ], _2 \. S N3 M) g - srv_addr.sin_port = htons(8888);
) m1 z* D E' h9 O - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
( \2 A, G3 P5 l - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
, K! W% ?8 A! x5 F4 s2 @0 l - {. `+ h% w: J4 }( d
- perror("connect");; z' k4 D: b% d0 r
- return -1; //exit //pthread_exit
4 m/ U, f2 n- V3 v. m) O - }
$ D* H- s5 ~" F2 F6 c - printf("connect..............0 |' \! B+ U9 @" h! m" p
- ");
7 k) u% A! q' p; B$ Y7 y - char buf[100];5 A' E E, g1 T3 q
- int ret;/ L% t) g% W* w& n2 `
- while (1)# T7 _6 D. M8 C; s- Y4 [
- {& I6 \# U2 [2 i. j+ _3 P
- printf("send: ");- e3 M& ]) k' u0 X* a3 g
- fgets(buf, sizeof(buf), stdin);
1 ?4 z B8 u7 D) b( v1 N - ret = write(sockfd, buf, sizeof(buf));
6 O' U. b7 n9 I ~3 h; R - if (ret < 0)7 Y2 h5 n5 {- w& t0 G; b
- {& q0 W) O: h: f, k
- perror("write");0 C, Y# y' V& [
- break;4 y- [+ X" _4 s' h5 T5 m: f
- }5 r* r3 z4 _) n" X- v4 I7 j6 I4 E* w
- if (strncmp(buf, "quit", 4) == 0)" I ?8 Y. D/ r& d- v6 `
- break;
5 E# V9 Y4 z. K _& t - }
3 x {; }2 {) ]# p0 e" i - close(sockfd);
h |& m6 b+ M/ u+ M3 E+ Y - return 0;
( a5 Z, j6 V+ F) |! P4 ]! {8 j& p - }
复制代码 5 T- l7 l7 U! N' g$ |
0 ]. g) {1 T3 n- e |
|