管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
* f. T# M# U0 p) K' U+ y* v
: @ u( j- h/ U
% n. \/ p* I/ s; B# X0 F% dsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" m7 D- _9 x( [6 ^; P1 F6 d s
, K0 D8 b$ f3 U, n
" k5 s8 X) }: W. j STCP协议- U' R3 ~ U0 Y3 e$ I+ w
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
- Z! Q8 [. Q- L/ `8 W% R: |
N6 P- x b" C+ p# F- J6 I i4 ^) ^( F+ d7 _$ U( z A+ F! L. a
关键词:三次握手,可靠,基于字节流。
+ P+ [& K* w% `! u: H2 G! s: s1 F; N/ q4 Z4 B
* K, \' S) ]1 ^可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。; K8 l. \6 p& I. k& }5 F" D
9 V9 \% P, k! { f5 m9 TTCP服务器端和客户端的运行流程
. E" q4 b" x4 W8 y- D/ H
0 T; Z/ M& r6 t3 N7 V( U) d8 A9 i$ G, j
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
8 X4 W7 w$ t0 X& @
2 j0 W* F! y7 s# I2 e
! ^' K8 F+ c2 o4 ~" n1.创建socket. d+ R7 G- G$ ^/ p
socket是一个结构体,被创建在内核中) g. u9 q2 A' y. p5 s( Z
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议* G) |" o4 Z1 s% i8 R2 a1 m
9 E, u+ v; d8 f, g& `# M9 o1 {' A' c
) S: E* g1 g- F2 M$ k
2.调用bind函数9 A( x: M8 z, z ]
将socket和地址(包括ip、port)绑定。
" c8 v' u) `; F e T 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序4 l/ Y9 v c4 t' p' s# n
struct sockaddr_in myaddr; //地址结构体
. c5 n( s P4 S; r2 t bind函数+ B5 E% n. A" E+ K
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))& M [) I' A% C% p
' f6 F* k4 ?& p) d/ Z
& L6 j; o4 G' T) z* B- o }3.listen监听,将接收到的客户端连接放入队列
0 Y8 y" q0 u V6 A; M% J) c listen(sockfd,8) //第二个参数是队列长度
+ ?, x7 P8 o4 Q" j& c$ ^
8 _/ } q$ l5 \( \3 J
. R' A/ y( `6 w: w4.调用accept函数,从队列获取请求,返回socket描 述符' y$ H1 f8 f# B8 R0 X% _' Y
如果无请求,将会阻塞,直到获得连接
0 d) M1 z2 ?! k0 `; I int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
+ O1 N4 K: _. @2 E& B0 n$ Z6 n; X% Q) Y* `% @+ [- ~/ X1 s# r
% Q7 ^: d+ V* E" ?9 C, P. S( `5.调用read/write进行双向通信
: U' f: y t$ N& s1 a, c) @* \7 C7 b l: E+ O8 ~ Y
$ u' x2 m o) X1 k7 y& ?0 G
6.关闭accept返回的socket
0 s7 \* P$ w, w& l( a; R! n close(scokfd);
1 p4 O/ f6 a! @4 L
W- f1 Q# H- X& F
2 k9 d: W5 u* L" M5 g B Z; h8 g
% G. B) C( J4 K- n/ y7 N
) T G1 }4 t) Q# I下面放出完整代码
8 {5 P1 p5 r. [+ B4 p
6 ? H% o3 r2 w! N) N. O- /*服务器*/
; O5 A+ b7 t7 u3 n. ^' K - #include <stdio.h>
8 m! k* y* q9 h - #include <string.h>
( h7 u6 d, i! k0 ] - #include <stdlib.h>
* X: ^+ b5 `" I! C1 f' `) L: W( R - #include <strings.h>, S: t( u5 A$ s
- #include <sys/types.h>7 G) c2 Z' M/ E4 A" Z
- #include <sys/socket.h>: q. J, d+ p4 [. {! x2 J5 {
- #include <arpa/inet.h>/ G* l! u1 ~" d
- #include <netinet/in.h>- m8 F }4 h+ ~3 e& E0 c% W
- int main()
' X0 u& X5 p' g! U U( V9 e) F* V - {$ T( \' c' m& e+ w8 A) B& f
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
' S' j$ P! l) v5 Q/ c. t6 j - if (sockfd < 0)" W0 j; G* s F" G
- {
( f9 H( K5 D7 Y5 Y - perror("socket");! g* A# ? |* }
- return -1;8 |% h3 j% M* M3 P- L
- } //创建失败的错误处理
/ D, t! g1 T& [; ] - printf("socket.............. F! n6 y- X, @3 v& h f. @3 S
- "); //成功则打印“socket。。。。”
3 G1 |; f" ?) W - 1 [0 l! |' Y7 y2 ^
- struct sockaddr_in myaddr; //创建“我的地址”结构体1 W, T4 f D5 s) H" Y7 D
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
" h& z7 `4 G/ B9 L - myaddr.sin_family = AF_INET; //选择IPV4地址类型4 M/ c2 d0 c( `4 h1 K
- myaddr.sin_port = htons(8888); //选择端口号. F! J5 t1 b& b0 k$ Y* m' P$ G) d
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
2 E& k9 x6 Q- s) W/ I t" } - 1 D8 s, H3 ~0 S/ O1 ?' \# B6 S5 f
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
$ ~% Y R, _; V& a$ H1 h4 }4 Y3 B - {
9 {6 ?0 D/ s0 \9 B5 J: C! B - perror("bind");
/ F" a% U! l+ e( N - return -1;: V& H8 o$ [# G* [9 C
- }
( G& y6 u+ Q' h# j; i" l' { - printf("bind..........4 a9 v' R. Q, k% M& n' O3 P* ?
- ");/ w: P3 c0 h5 N/ c; V$ e
-
; h* b$ c- [$ c9 P% S - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听9 s1 y" G9 ]0 s3 Q; F
- {- d# y7 v+ d! z" y: J5 H
- perror("listen");% G/ [5 ?6 p( z) J+ w
- return -1;
1 L' |0 m+ K) x8 x, n. d - }+ i+ |& L) X4 r) B
- printf("listen............
5 L. `8 t/ r$ t0 A - ");
' Q% r, d$ [0 \7 t -
" w9 q+ P2 Y! U, [. e* J - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求1 b4 _: C6 V5 W. m( X8 C
- if (connfd < 0)
" r: y O) O# w- v - {3 t* R8 f6 a& d9 _( ]
- perror("accept");
+ q* {& O+ a9 N7 Z+ @+ r7 I$ H t - return -1;
8 Z& F& E! K1 o) D+ S2 p# p$ V - }
, Q( D# L2 g! M# f - printf("accept..............
. q9 `" L9 |! g4 t; B6 K0 t - ");) Q2 w( l0 Z9 P, A
- char buf[100];//定义一个数组用来存储接收到的数据, ^: D( m- c1 b' z* v9 L2 w$ G9 a
- int ret;
! _: X% l$ B% Q" D: s% v - while (1)
: `& h" V# f2 ]% } - {. ?' ]* r6 Y: Y
- memset(buf, 0, sizeof(buf));
( M( ]0 F# j. q, Z' s - ret = read(connfd, buf, sizeof(buf));
( }6 f2 R' D! B# Y+ S j7 q N - if (0 > ret)
& M2 {+ d! F0 K: [; y& Y- a/ L) T - {
; q6 L$ }$ j9 ^* f* d - perror("read");
2 X* l. w2 Q# V% ^2 H, N4 X. l - break;
e# }$ M. C: ]7 ^ - }//执行while循环读取数据,当
7 P" i, h' f+ s7 j b2 J, v x - else if (0 == ret)
+ y4 Z7 F; S$ R$ l - {/ R, ^) E& x/ D. c
- printf("write close!& i6 l, W' }, x! H6 o8 [% C. o
- ");3 h5 L8 D9 g, f3 S% q
- break;
# ~) Z" W/ [8 k$ c; N* X f - }+ o3 G/ j' J1 d& x5 [% ]6 s
- printf("recv: ");
+ ]2 Q" d# ^0 k) p, n2 N - fputs(buf, stdout);//打印接收到的数据8 ]* h# r$ _, r) s8 O* a# j
- }% W% U% Y W% {# \
- close(sockfd);//关闭套接字8 T& r0 V& S+ G8 h7 r
- close(connfd);//断开连接5 q3 t2 k$ c( ^! h4 A K2 P `1 I
- return 0;
" Q5 u+ D' v6 k# {8 f - }
复制代码
% p; e) e; n0 B. L
1 E' K. x$ f# ~' `- /*客户端*/(具体功能和服务器一样,所以不再加注释)
5 I. {+ ?$ j8 T# v& j5 k - #include <stdio.h>
* k# U& L$ `5 n4 k1 o - #include <string.h>
6 ]( J. ^# W1 M$ \# }& S: F - #include <stdlib.h>
" B9 s9 K$ ]$ f5 [8 h - #include <strings.h>$ v# u8 C3 F7 B* w. P! F
- #include <sys/types.h>! a! h+ m8 R0 v4 M* H O* l' t
- #include <sys/socket.h>7 e1 S5 g% c1 l8 \
- #include <netinet/in.h>/ I7 y9 m- V8 u) f
- #include <arpa/inet.h>; g' I5 w/ p/ N: C! S, f
- int main() n0 y- d$ [3 f0 i' E' c
- { |, O% P& d; l; O
- int sockfd;$ |6 x! ?( T& k4 A
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))); N8 V, V I; {" Q+ X1 J( E; v
- {- m8 d, w1 G, n
- perror("socket");
n0 ~7 X) e; h. @) y3 I - return -1;
8 \7 ?# f1 `1 s& T% l1 ^2 U - }
& ?% _# g* p. K( c4 l - printf("socket...........8 q8 K5 ?" k) O
- ");& O. V5 [: z+ O, _# H* S1 z. O
- , a! x6 y$ }1 u& e/ w- T$ F/ r
- struct sockaddr_in srv_addr;, C; r' ]7 `" a( s3 @+ ~. A
- memset(&srv_addr, 0, sizeof(srv_addr));
5 a& \, w( Q8 E; J9 y - srv_addr.sin_family = AF_INET;) i: ^" w' \8 P8 W& ~' ~
- srv_addr.sin_port = htons(8888);' p: N$ q( v( }. f( D' U
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");/ N- b3 l2 @! @3 V. Q! s9 [) [
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))3 J2 z7 {" t' ]# ? D
- {
% b1 R( W2 r* `* ] - perror("connect");
0 }2 c( h$ T$ k - return -1; //exit //pthread_exit! v+ I8 {, x0 n4 f: l( z
- }
$ M3 K' d3 \& C5 Y' Z L - printf("connect..............
0 w- `! o" i, G - ");
4 B6 _( A! [7 N" \6 k/ M - char buf[100];, ~% F8 H4 o" F/ [
- int ret;
' P G! r0 o/ D6 }/ v - while (1)* X1 q v7 i3 B6 T. [3 |; b; ~
- {
c1 m1 p! h* S6 L9 P: v# {( e - printf("send: ");
$ M6 I1 f8 e+ s/ K - fgets(buf, sizeof(buf), stdin);
6 s8 I- ?! l5 [- g6 G) R; N$ g" z - ret = write(sockfd, buf, sizeof(buf));2 w9 G4 g# \8 s" o+ u( l
- if (ret < 0)8 t: r! I# m( p3 D& O
- {
0 _! `6 ^7 I1 d - perror("write");5 H. p4 r9 ^3 [3 q
- break;
\" H$ x% B! S - }* J: W' C) h; k V
- if (strncmp(buf, "quit", 4) == 0)
% r0 Z" V. U- G2 W - break;, ]2 Y$ c& c4 a& G
- }* y3 e6 E0 h) _- v2 {
- close(sockfd);, ?( V: a8 g; U0 A" F' p
- return 0;
& d, U+ I6 E1 M) x - }
复制代码
( I6 `$ i; l3 k o/ r: N4 U6 B8 ?, }% }' @) x5 Y
|
|