管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
. q2 L: I' n! m
* r) X) R, b( J( p1 Z* d$ J- j, ?2 |7 p6 X! k A
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
8 u+ w/ |. }3 f8 O& K6 A# ^& @# a0 `8 |8 G3 v+ m
/ O/ F" {, Y% u3 D! A
TCP协议8 U; O2 N; z" R- N! m
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。0 F) l, v; |: l: {1 g+ `
, I. }1 s7 \; n$ H L
# }0 o( i q- q# k关键词:三次握手,可靠,基于字节流。8 \8 X! ~# }' T/ _6 r. n
6 E+ K* i* I2 U& @1 w6 I( k
+ w# o1 h0 w1 O. e2 z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ k; I7 Z; \9 w9 `! B* u% [' h5 T. ?
" I6 f, D3 n+ T5 s* o {
TCP服务器端和客户端的运行流程5 y. {# i, `7 o; G1 U
5 l5 L: X4 v, f5 p- k5 o! C# n3 U( D: O4 M6 `& P- H; ]
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?* l6 F- \. Q( f! A8 m; O0 r
( w) }! r7 x3 a
$ S1 ~' Y6 n0 M1.创建socket+ ]9 N# {) R- O8 b; A, `
socket是一个结构体,被创建在内核中
& t2 | O! M0 o: c' E' k; o sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
- ?+ P' t) m6 _2 {4 U7 f/ u1 S9 E: o" U/ k9 m8 V
$ M; E" k$ A5 X; B3 s. r ~! I5 C
2.调用bind函数! h# ?) q* D2 B& _( O
将socket和地址(包括ip、port)绑定。
* ] G, n9 w- x& {3 m 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
# w+ d- q* |1 X; x. p struct sockaddr_in myaddr; //地址结构体
8 x, z7 o4 w9 B4 h2 y& X" E+ J3 c bind函数
$ K' t- w" l, K& H) z+ T. n bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
+ D$ A& m- L$ b y2 O% Y4 Z( |. S& X& B- s
" b+ B W3 _; h1 }" Z) F) N u
3.listen监听,将接收到的客户端连接放入队列
8 ]' |- o5 |7 \! E$ L2 _ listen(sockfd,8) //第二个参数是队列长度
1 P6 {4 V' ^) ? k
( l# B/ t! f. t* X, }$ ]6 u
- x4 l, y6 c! n+ T& J& [* l) l( Y4.调用accept函数,从队列获取请求,返回socket描 述符$ d% C5 ?; z( B0 d* x
如果无请求,将会阻塞,直到获得连接
2 t) z9 Q% s t9 z$ V( G1 v/ Y int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* J8 ^3 h/ F9 D2 i5 b; _/ f+ ?5 Z% m' L2 F$ e+ b
/ |: V- G. T' d% p2 q! x' N5.调用read/write进行双向通信
2 ?( O0 P8 ^: g% }0 x
7 b( _' c) Z6 u: B# C/ W' L! o+ J' }& L
6.关闭accept返回的socket
& n/ R K2 d6 f close(scokfd);$ H# W" Y E1 ]6 X
; W" m* {' S& \ {! v& g9 i) f2 i7 `. p; Q9 K Q
, ?. U' w2 |- u% b
) H1 q" T/ X X' }+ \8 H& {: {
下面放出完整代码
2 a1 o6 `/ J) G* X, d8 a0 \! U T* k9 _( e! O3 U
- /*服务器*/% J% ~1 {" ]! Q" X) O
- #include <stdio.h>
( j/ l7 v8 l/ I9 y- D6 T - #include <string.h>5 @9 j. ~2 M2 E3 d3 g- j1 o
- #include <stdlib.h>
3 L2 H* u& M1 n" f - #include <strings.h>/ E& S) T+ H# J- s6 C2 V
- #include <sys/types.h>
) |$ o [2 b# E7 H) R - #include <sys/socket.h>4 ^6 B1 L6 u; l
- #include <arpa/inet.h>
* e8 U. H; G/ J+ ]* G) n - #include <netinet/in.h>0 k2 Z8 R$ [0 B
- int main()
# E a+ z7 s0 \; M+ ^# o - {
4 S" D2 h% A" E( d6 Q - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
* m! d; f3 ^$ Q9 w3 {% k - if (sockfd < 0)
) m: @ K& k( @& g - {* C# m# p" E& p; m4 {4 x2 ~
- perror("socket");
- ^ B1 {5 Q3 |. }* L - return -1;
3 @' C. j" a+ x, ], ` - } //创建失败的错误处理: `7 P, z6 c9 V& W9 d/ D% s; d4 E
- printf("socket..............
/ t5 s3 t A: h$ E& h# h1 a - "); //成功则打印“socket。。。。”
X8 N; v, S0 o+ _" I8 ?, N - ( s) `) I n6 _ I4 A* F0 ?
- struct sockaddr_in myaddr; //创建“我的地址”结构体3 B9 E! ^& B6 w9 r1 x- p1 x7 Q3 @2 S
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
( h/ A. _0 b# b7 y# I( P0 A5 t1 Y - myaddr.sin_family = AF_INET; //选择IPV4地址类型5 q& P" Y4 a" j/ F
- myaddr.sin_port = htons(8888); //选择端口号
3 v& t0 z! v8 r+ E, ~ - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址% b0 Y2 l. T; } b
-
1 n, b3 G1 Y l- o2 h$ ^( q/ U - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
# M. Z( q6 L; ?8 x6 s! { k - {9 ~% D0 n9 c& n; r4 @9 r8 l0 ~$ w5 L
- perror("bind");
; m9 K- z$ N: ?0 A% s$ l) ~ - return -1;6 v' n" \. Q7 ~. w% U% k
- }; Q, k. N- }; N
- printf("bind..........
" L L1 k# D* w* p1 g, ]: `3 E - "); i9 ?& l6 E' N. y' {) d- ]
- 9 D( L7 e- [/ x1 K2 F: Y# T% d+ {
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
- C/ A4 B- v2 t$ k2 W+ h - {- |- i" w# j. H. ]! I' B
- perror("listen");$ e# v& P" w. t4 j; n* _9 m' A
- return -1;* h* E4 m1 d) t' t
- }
( f/ [' A$ ^( b/ K - printf("listen............2 s; N c4 p* Z* ^
- ");
6 Q: `8 U* F* m -
" {0 z% c6 W1 I5 m- ] - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 C* Z# ?/ @& p
- if (connfd < 0)0 D5 L' t; @7 u8 F _
- {
2 ?6 N- _. Z. {$ l. L- I - perror("accept");& N# \. Q6 w& f
- return -1;- e& z+ b- }2 T: D
- }" o. w# h- y4 ?2 z& q" m! F
- printf("accept..............
4 w. u0 n8 `! q/ k - ");0 P& x: _0 m, g. ^# J
- char buf[100];//定义一个数组用来存储接收到的数据
- j' l( A$ x- e& c1 S3 ?2 z$ G0 I3 _ - int ret;6 W! [) W9 A6 W- i# o
- while (1)
% H+ D1 U/ I0 g6 b7 ]2 i% f - {
% L3 ~; j' {) q" G: j- H - memset(buf, 0, sizeof(buf));
" r( u! \/ T/ _8 x+ c- w) ]4 b - ret = read(connfd, buf, sizeof(buf));& m+ {9 R( j7 I5 C
- if (0 > ret)
& f1 J1 b' R$ c4 ~. ]/ H% Z( \3 M9 c1 _ - {3 L, B2 k$ t% w% }) y6 N7 \
- perror("read");
( P- k" M! [1 U; D7 Z - break;/ {2 _$ C+ w! O6 V; n
- }//执行while循环读取数据,当
1 p }( n6 B* g2 w - else if (0 == ret)
2 Q }9 N% ]- ]+ y3 w# l5 [( B - {! q, y- @* r# ]1 l% Q# {$ u" j6 ]
- printf("write close!% Z9 B* S3 g* Z8 N
- ");
0 ?* D+ h4 B+ g, L6 L( I - break;
) }5 d7 K, L+ b; K8 Y! z( y( | - }3 W6 D) i5 d# W' j: c' |
- printf("recv: ");
- E( N, X# P r+ @7 n0 k - fputs(buf, stdout);//打印接收到的数据) g9 [5 S( V$ f, K+ l/ L
- }
3 i! A2 v" }% _- f - close(sockfd);//关闭套接字
, P m' L G6 b* w$ O - close(connfd);//断开连接: t6 I; L, g+ Z3 o1 J3 |0 D; @0 p: X
- return 0;, D6 C; q. i( G+ F1 Y
- }
复制代码
! y9 T. H6 q: G8 B; l/ a. i5 C1 i) _" n. t u! c( Z+ B R# ^7 U& u/ V4 W
- /*客户端*/(具体功能和服务器一样,所以不再加注释)1 u) n$ @" y, _ A
- #include <stdio.h>: a7 }, F8 I. k* G6 _; [# m
- #include <string.h> k! g. _. v9 u3 T
- #include <stdlib.h># W" O" D* m8 d
- #include <strings.h>* k3 @* Z) I) U% ^
- #include <sys/types.h>& C1 Z0 p1 S% r$ J# V
- #include <sys/socket.h>5 a- R8 q- W; ~4 g
- #include <netinet/in.h>
7 P8 U7 V% L0 u: r' o0 m' y# w - #include <arpa/inet.h>% Q& ~; ^7 k' Y$ h+ n
- int main()
, a9 j. Y4 l' R7 M/ t* c1 g. L5 T - {
" F2 v, A; K% _/ ?! D; k2 A - int sockfd;7 P2 a; b& _0 k! @7 F7 U5 p3 E
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- t1 {3 c+ P# M8 O
- {
( I: R w1 {/ F! H+ p - perror("socket");
* n( K2 t1 z; ^# Y6 g/ R4 ?. j - return -1;/ s1 ^0 n- A5 b& s# W/ Q% s1 \
- }
- v' o) K. E& Y - printf("socket..........." Z8 X/ e: _# |
- "); x1 c/ y& K- d* t; D) Z
- 2 c7 y/ V8 _" e. O3 @
- struct sockaddr_in srv_addr;* |0 q' ]' c) m3 ?- U" O. ]; |
- memset(&srv_addr, 0, sizeof(srv_addr));
]1 S* L: Y+ i" a2 _$ a( [ - srv_addr.sin_family = AF_INET;
" w# d# q; W E" I# k- e - srv_addr.sin_port = htons(8888);
0 s4 ]; k! B8 E* R - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");# k% h1 f" W. x) z* a
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))0 a3 X: h# N4 y4 @" I" }
- {. y$ B8 ~' F* h9 o% h) {
- perror("connect");
. h- Y# {) {& @/ G - return -1; //exit //pthread_exit
f& [' E2 G! F" D - }
, W; N2 L5 @9 k/ E/ k - printf("connect..............9 }; ~7 n) K4 Z) x9 M% ~' [9 ]
- ");
( I5 U: {; S$ B - char buf[100];) P# W4 ^! G) b0 o
- int ret;
! v/ |( b; @& a) e9 [ - while (1)
" a+ s. b- `, M - {
2 S3 S0 F: v) }+ Q: d, C/ y - printf("send: ");
) T9 h& T8 T4 t- C5 f" p - fgets(buf, sizeof(buf), stdin);9 N3 V! z4 x: b. q9 o. j3 W
- ret = write(sockfd, buf, sizeof(buf));6 {0 Z4 @: b4 j1 n2 N3 L$ }
- if (ret < 0)
% J7 l( u5 x6 k& j6 F0 m/ C) ^ - {
8 q" y- X# v9 }* k$ H - perror("write");
( N. X" n6 f/ ^" v0 N$ ` - break;
5 R5 u5 i4 O& y3 R0 j5 D/ ` - }5 [4 v& m: ]2 b, S3 D- R6 v. X! A* S
- if (strncmp(buf, "quit", 4) == 0)
2 X3 l( |* C; B6 h) e; o+ |/ G - break;; U4 I" U. M& s) q- ^" M; r
- }
7 n, X( l: p2 U: Y - close(sockfd);
8 T! e/ A1 Q4 W; a1 n) t* y) U - return 0;
& a) j4 o1 i' T0 e - }
复制代码 ) `, }8 R+ C- u
# M7 F7 G( p) X Z! g- g9 f* Y
|
|