管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
" C4 O/ L0 o0 l% Y5 _! i& r
j g+ S/ w1 S/ Q9 `4 ?% n2 T' q* e5 k5 _) _# Y. g
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。: a0 y8 B+ h7 E- a! |
9 h+ D$ B3 @8 p4 o
- }, a! |0 j2 U5 U4 FTCP协议
# ?) W a; O. @# yTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 u- Q( y- E- | b- P
3 g) q1 `% L7 k- x, h0 a- C( Y% w8 X6 b* ]
关键词:三次握手,可靠,基于字节流。
0 L* @3 Y' O. e" Z6 y }5 n6 p. o L) \$ a1 T9 K
/ c4 L* x- O; C/ G1 @# {. G/ {5 Q可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
0 f/ }7 g c+ X* j2 |
& s5 C5 T3 u2 \7 B8 O* _9 d2 U
TCP服务器端和客户端的运行流程; {% m, q- c: z x* ~1 f: b
2 h @5 S8 ~% W; `5 v
. b6 u4 ^( r J$ k如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?2 D. w0 m0 R* [* v: o9 \
0 q( p" b+ O) K4 \1 g1 N
# k, k# ^3 T: H; u( Y
1.创建socket9 Z' w* C6 F* c8 V
socket是一个结构体,被创建在内核中6 o" N5 w) ?& n# s ?( E1 }3 e/ `$ E
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2 z; b$ U) @4 A3 x% f
: l# u- `1 H: B
3 @4 r* a1 ~7 L+ O' U2.调用bind函数
6 I# @$ S" m6 j 将socket和地址(包括ip、port)绑定。
& r% r# A4 d& M 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序 z: k" |7 D9 r1 X- W! b
struct sockaddr_in myaddr; //地址结构体
2 W# i! a, m3 B* l& G bind函数/ t9 E3 W% k- B7 v9 x2 X2 L
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))0 Z2 X Y+ `4 r
* b X) o, j- z. d
9 X6 k M) B1 x
3.listen监听,将接收到的客户端连接放入队列
; t0 i' i- D6 [- ^: F6 ` listen(sockfd,8) //第二个参数是队列长度7 M( U) x1 o3 V8 h% X1 w
8 E4 w! F* X) U: _
. W# B D( N1 T$ D0 n a
4.调用accept函数,从队列获取请求,返回socket描 述符
& j1 r. \2 t" B8 s9 h! Y 如果无请求,将会阻塞,直到获得连接
- Z J, x7 E! m1 a J9 c int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
_: `: M7 _0 j- S+ i( @/ m
- B! V8 @" c. V6 S4 [5 u+ B( C; h8 R, Q
5.调用read/write进行双向通信" Y( C* t7 b3 n, L9 t: G
9 ~" R, y4 W9 F$ Z2 o. B
3 _+ t: J7 R! ^& D; W6.关闭accept返回的socket
9 M0 U& t% Y' Z* h close(scokfd);2 k' [ {4 W( Z( L, Z! I( |3 M
# C1 z& i4 d( F: E* X/ k* E2 O0 c) Q
- ^' \" p$ @: d$ k7 f" |
% J5 {8 E4 Z; p4 \% ]6 P8 a" ?" ~4 ^
3 f' Z! s, d& {3 z2 L: B+ G下面放出完整代码
, e# X% d/ P! w/ U/ |) C" ?4 I
) N5 J) z1 w+ }4 V- /*服务器*/0 l0 q% J& P5 Z) Z- l. ]
- #include <stdio.h>
0 Y3 }8 O: d; ^" t; d' \ - #include <string.h>; i$ @5 k4 G6 W, T3 Q, ?
- #include <stdlib.h>1 F2 I" F5 \3 j; U. l
- #include <strings.h>1 o6 d! l& z; ]* w2 S/ T( S
- #include <sys/types.h> s4 y" M: Y& n) R7 B
- #include <sys/socket.h>
2 X& f+ R* O) W* {9 z3 T$ f- Q }# H - #include <arpa/inet.h>0 z$ O2 L# ?4 k8 r
- #include <netinet/in.h>
0 y* y, g, ]* b1 e% d" t - int main()
4 y. e9 Q3 C& _4 R - {
$ K9 ]) u3 ^4 q1 ?4 p - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字& y/ t" z: R5 ~* y. V) N( Q+ h
- if (sockfd < 0)% c+ `0 J. a4 W, V8 r# I
- {
4 ~! ~$ u$ Y! e. ? - perror("socket");
8 c' b/ f. s, g0 }2 d - return -1;
: S: V+ x( n) w, W' ` - } //创建失败的错误处理
0 L6 I" @; E3 t9 W0 S* A+ { - printf("socket..............& b# ?' b2 I* t# } d. h; f4 @
- "); //成功则打印“socket。。。。”2 L3 I. [& ~0 F4 ~
- v# a7 e7 u3 P" w, S. q
- struct sockaddr_in myaddr; //创建“我的地址”结构体
+ ~' e c8 O. L( U: g; z - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)0 l( M5 V9 r" Y. h
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
k. J$ G* V4 Q$ [ - myaddr.sin_port = htons(8888); //选择端口号. r" c" F' Z |! M5 q7 Z
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址; k' v O; k& Q2 X7 z2 U0 I
- # D4 Y, K( M- T! D/ |
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字8 W5 w1 ^4 ^6 M! O
- {
+ ]& W4 d. b- w5 F; E+ B, T - perror("bind");! C- N0 [' \. x: y/ r
- return -1;+ s8 h$ q' _7 }: I
- }
! a/ Z* G- `/ I - printf("bind..........
G- ]' j( \; Y& Q" b3 n - ");
/ G8 S% z% P/ k5 K$ a8 @* V - ! D/ a( ]5 L: Y7 ~6 B
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
$ U o4 \0 G9 N4 E - {
* {' h1 K6 h% [5 _7 g6 {* }9 m7 @ - perror("listen");/ |4 N9 d. B1 @
- return -1; ~6 C P" ?8 S$ h/ k6 j6 D
- }
4 \* s2 e" j2 P3 n, t - printf("listen............# }/ z$ M1 U: j0 `) W. V4 q: ~' l
- ");
% j |* b' O+ E8 Q; C5 [$ p! _' x3 g. O - 8 E3 y8 H, t, M. M/ O) y. \
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
" W; W$ `% V& ?- M - if (connfd < 0)
/ o G' I* D+ A: ` - {7 l9 e5 y A" ?" @- t: F% ^
- perror("accept");( C# W1 D0 f* ?
- return -1;
( g' z8 @) q M5 y$ R - }
6 i8 l" v0 \ N8 J; c" M0 e9 E: _ - printf("accept..............! s6 ^ u" X( B; G, P
- ");2 _) c) ^# Q& c# h* }
- char buf[100];//定义一个数组用来存储接收到的数据
2 r3 _7 _- n6 Z( p; l- H3 N/ [ - int ret;; R% Q8 q4 K1 Y* Q7 D0 `- j+ s
- while (1)
8 u8 g3 Q# O# X - {
+ ?8 h y2 g3 k" M5 ] - memset(buf, 0, sizeof(buf));, x, ?7 J- i+ ^$ P( d) d! s' j
- ret = read(connfd, buf, sizeof(buf));) l4 h9 r; Z# K0 n; N" G6 I3 j) M
- if (0 > ret)
% H& P$ K% S( H( e& Q - {( G: U! r# c8 ?. b) D9 }
- perror("read");" k: m8 u( w, n; M$ R# I# q4 {
- break;# |$ ]% i" e& z6 ~- X* M
- }//执行while循环读取数据,当
6 f7 {2 k! f, j( m9 S3 O - else if (0 == ret)
' @# G+ J8 i% _0 b4 I5 o5 h# K - {
# z! ]9 u% Z: X% c, R# q- K - printf("write close!
! b6 a0 q+ a9 e8 F - ");
. R& X6 ^4 ?' Q2 n, w& B - break;! M# ^3 M* A7 L. J. m- j2 l% V( o8 T3 y
- }7 `% m$ V( {8 X/ y
- printf("recv: ");& z6 H6 A7 J3 n
- fputs(buf, stdout);//打印接收到的数据
J9 t* D/ P, |' }5 m7 Y0 e - }9 d9 s3 j. w: [% [
- close(sockfd);//关闭套接字
9 q# u; S% C, l* G* s2 _1 v- V; [ - close(connfd);//断开连接
+ Q( E# j, i: g' }/ J2 I7 u - return 0;
- e+ G2 E2 o$ n2 A' r/ E2 m - }
复制代码 : |) z. V6 e- u' r; a7 @
/ Z |+ `, ^' T- I W9 U- /*客户端*/(具体功能和服务器一样,所以不再加注释)5 F1 q8 b5 I& z3 t; w
- #include <stdio.h>
9 w% p2 G( O2 N4 j& E. q) @( \ - #include <string.h>
2 `/ d8 T* {! g3 P9 z - #include <stdlib.h>& p/ v) F& S- ?/ ]% }# q
- #include <strings.h>* D$ l8 u- F0 V: w
- #include <sys/types.h> I" H. K, Q) B' Y' b( ?+ \1 ?
- #include <sys/socket.h>
$ j, r! r$ R4 X( p - #include <netinet/in.h>
! G* n3 y5 V0 L/ i - #include <arpa/inet.h> z' y, Q3 w1 P2 A& N! h; B
- int main(). W8 J; X) P, T& h* q" r3 n/ @
- {8 ~" D0 b3 Z3 V" I8 J# g
- int sockfd;
" h8 k& q# V! D+ S - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))9 @% i5 [& _( X( o% Z' }1 C" b
- {8 K+ L% @3 W7 Z9 \, S5 D& d
- perror("socket");" s# k/ O* g. P/ C7 R0 X
- return -1;. e% H3 O5 C( k" }2 I! Q
- }# E$ p* |! ]( B; L6 n* P- ?5 Z
- printf("socket...........
6 ?: R0 @; P+ c* P( E9 i2 ` - ");
N9 _: W8 N) F3 L' |4 S - ( N/ g* T- X( Y+ H+ K4 w; j
- struct sockaddr_in srv_addr;% |, \6 Q' z( W2 w
- memset(&srv_addr, 0, sizeof(srv_addr));
- p+ s- i+ s5 Q9 @8 v' \0 i - srv_addr.sin_family = AF_INET;: a" S+ b `4 n8 N
- srv_addr.sin_port = htons(8888); l& K! I8 z. A& {# t8 C
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
. j& [# u. H! T7 K - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))5 i! W5 {1 J1 u- ~& e
- {3 k. f, X6 @9 j( k! ?
- perror("connect");
$ x; j8 [5 _% r9 S7 B. K - return -1; //exit //pthread_exit
0 J8 W$ O. {( G - }
$ a/ ^4 A( h. |/ ^) [0 }: M$ r - printf("connect..............
. p* P- t. l9 r - ");
7 c* A% d a. [4 b6 o# l8 f7 e - char buf[100];" E, C& v3 z7 G3 P- e
- int ret;# a' b8 V2 ^& A2 ]: P6 {# \: J
- while (1)
1 \0 a) ^, P7 i! {& c - {
o! l% Y6 c* U3 }0 }' L9 F( a6 Q - printf("send: ");
, k. J* ^1 |; U+ v - fgets(buf, sizeof(buf), stdin);
7 `- `/ M% p+ S5 i) \7 h; e3 [8 \$ G$ ] - ret = write(sockfd, buf, sizeof(buf));
: A9 w# f: `: p9 w - if (ret < 0)
/ ?. A; \6 F0 | - {4 g' `: I4 s' [
- perror("write");
/ l8 D0 ~9 D/ i4 a8 n# N" }. W - break;# O, B4 O( v* \! R
- }
2 t6 W" L+ d4 F: m) Y - if (strncmp(buf, "quit", 4) == 0)7 ]9 t' u/ P! l4 z' j
- break;
* U; J& X! X2 Q0 R% @/ V3 k0 R - }
0 S4 @, i# S1 F6 s& d a# @; U7 Z - close(sockfd);$ O" Z( }+ s! a# |
- return 0;
0 S; R$ M+ e% ^! [4 _+ y - }
复制代码 $ o0 C! S8 Z6 j( n7 R& F
$ g, d2 O, L6 b0 i. P, ?/ X' p& @- n
|
|