管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
, v! \ o* A: \3 I( _' X) I$ E7 W5 m( V. x# h9 ~; M: P
1 S: Q0 W* {3 C+ v4 U* Wsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
0 N) ^6 ?. F7 `
1 z0 I" @0 ?3 m4 y8 ?+ i6 S; U8 ~" g0 O5 P+ [8 }8 U
TCP协议
, r- J! ]8 K/ l* FTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
0 r; H5 X5 D) J+ U! D4 B4 \6 _. h( l9 \
6 Y+ q) T9 l7 g1 j9 J
关键词:三次握手,可靠,基于字节流。
# }% l% |) e3 Q. ]( m9 E' b5 c: f! Z- J, [
x. W) j2 D: [! _ K4 l7 a1 M可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ @4 k9 Z% r( E+ i0 q! m5 K
3 a: p" C# M+ a+ j7 }6 R# y8 ^
TCP服务器端和客户端的运行流程$ Z7 z2 ~& x4 s; [2 f3 C$ `
. m" B1 T- a9 n# R- u9 x. d* p7 N* u; [9 W* ]
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?0 Y1 _! e: t$ y! ~- L. x
5 N( Z& i! x- l. l) y5 D0 i( r
% {. v) w+ d I
1.创建socket
3 {# m9 }7 D- V socket是一个结构体,被创建在内核中
& p5 {# W8 m( s! K3 O sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议6 @! O8 q7 f( S5 l& u1 V% ?2 D
7 \& u9 L9 f) @% l9 X, V( {
9 J# l! F" X" D- @2 E
2.调用bind函数
' O& n7 \. l( J9 F& C4 a 将socket和地址(包括ip、port)绑定。
7 E5 n7 r, \3 z+ b1 r# P 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
$ X, }) K1 E! w& E7 r0 R# c. m struct sockaddr_in myaddr; //地址结构体, k. G- Q$ s7 k5 `' `
bind函数2 Q N% O# s1 A, V
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 d l1 A% n- X L
2 ~: Q3 K: P5 Z: H
. I# y0 Z% \ [ d* t& z3.listen监听,将接收到的客户端连接放入队列
8 d- `( G, z* K) b, E listen(sockfd,8) //第二个参数是队列长度
% F: d4 Z$ ?5 O& f5 I3 G8 M. o" D1 W4 e7 m6 r0 ]: F
# \+ H8 ~2 O; G5 Z8 e4.调用accept函数,从队列获取请求,返回socket描 述符( l) B7 }. W! ^3 A/ i( C. d3 t& y
如果无请求,将会阻塞,直到获得连接, m$ s" Z. D0 h8 P: W
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
3 r4 \6 A) R( T' ^1 ]8 |
. v1 q5 N, e% J3 p9 z% ~$ s/ s8 M7 X% B% I; Z( m
5.调用read/write进行双向通信
6 u+ E: j' A0 s3 j6 c8 L, x c( G/ w v
, c4 z( L2 Z* }) g) J; ?" ^; C- T
6.关闭accept返回的socket
9 l; P) t/ [9 u close(scokfd);7 k+ G, Y" q' G) N6 ^4 h4 F) ^3 M
$ A/ `+ [; i" ^4 V
8 Q2 A1 d+ L4 C- o h) l& X* Q [4 b) g0 B( w" _" V) }+ c7 b
1 P7 ^+ m8 T8 ?7 P' C/ W$ M下面放出完整代码8 x/ Q. [1 ^7 o8 b8 O
7 U+ N0 K2 g: [. g4 Q1 o
- /*服务器*/
$ V5 o1 O% {. x& a5 C6 M- ^ - #include <stdio.h>
' g* N4 a8 m# c5 J% B q9 z9 t" P- @ - #include <string.h>% Y# |2 ^& W+ g# u$ Q+ W
- #include <stdlib.h>
1 x5 b" o7 t/ G; ] V - #include <strings.h>8 S6 o0 J8 f! O) O' v$ \
- #include <sys/types.h>* x1 ]1 @8 q9 a" E9 k {5 M
- #include <sys/socket.h>' m5 v/ R1 E, I5 ]
- #include <arpa/inet.h>
" D, P3 f" O0 J% S - #include <netinet/in.h>
" F; ?% E# G# x7 c - int main()) Q% L4 R: I; f* ]+ O" Q; i
- {
/ H8 W6 R) P i - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字8 [" h9 W; E( v2 ~ W {! H: Q$ U3 Z
- if (sockfd < 0)% ?2 @, G3 E A- b) V- F6 L" g
- {- H0 r# u9 e+ T! s# i7 ?
- perror("socket");* e" r& N- [; B1 ?: T9 w: ~2 Y
- return -1;
9 S4 ]$ ]2 z( _# a - } //创建失败的错误处理0 m" c* i* r9 Q5 }' B
- printf("socket..............
% z5 h3 H/ L x - "); //成功则打印“socket。。。。”
! S1 d" F( C( R9 X8 x* s0 y4 w - / z7 B! Q; y$ x. y7 g
- struct sockaddr_in myaddr; //创建“我的地址”结构体
7 H9 ^$ L: d: S9 j5 }4 ?$ c - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)+ c* T( d0 P6 o) A; H- u
- myaddr.sin_family = AF_INET; //选择IPV4地址类型3 K9 q, Y% z8 h0 o! F7 h6 |0 L
- myaddr.sin_port = htons(8888); //选择端口号; _% B/ x d0 j7 p% E
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
; D r* g: d* }( w ~7 v -
' B8 g# [$ J$ { - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 Q8 n, L/ V+ k1 h0 X6 F* W0 a
- {
+ D. ], ]4 P) r) C# Z }4 Z. n% | - perror("bind");
0 Z( d% f; _2 N9 u( [0 m - return -1;) [5 ?5 K% }1 p5 S$ M# h$ V
- }
s: S$ t4 }( i - printf("bind..........
3 P' S+ w2 n2 ` - ");& o- q7 K9 j& ]/ l7 b
-
2 B6 A( Z0 v. ~) S( H [ - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
9 m" K$ S/ L- M - {
+ F0 ~' c, h: K3 m; e; n - perror("listen");! A! N5 S/ t6 d! h; ]* o& U/ L
- return -1;
/ w: F3 g7 T5 X& y# b |& X - }2 r# b. }+ c% M. [( q6 F
- printf("listen............: ^! B2 K9 r/ j+ P, ~* d
- ");
, s! J1 e! F, E; _( J3 ^ -
& p1 J0 n. a( c6 G* N - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ s8 x9 s& A7 J8 |
- if (connfd < 0)
: F& ?( r& a, [& r - {2 I3 l# a/ u/ F) ]
- perror("accept");. j0 c& _% Z; `" t( P! u
- return -1;8 j" F9 r* W# ^+ j; v
- }8 L& Q9 R l, n- i0 M& k, O
- printf("accept..............
5 O, U7 D4 B1 l g: @& ^+ z - ");3 q% L" [, v8 G" u. l* Z
- char buf[100];//定义一个数组用来存储接收到的数据% I6 f. [6 T; ]8 g6 L5 E
- int ret;8 m, `, I8 Z- `5 A$ A
- while (1)1 n: z- |5 p8 X7 F/ F# N
- {4 R7 o9 x- ~5 T. w. Q
- memset(buf, 0, sizeof(buf));
% z( W4 ?% e5 U @; X+ D% | - ret = read(connfd, buf, sizeof(buf));
! N( p u$ e9 ]3 k& b8 Z" _- ^ - if (0 > ret)0 Q( E+ g6 A, t% X7 i( [
- {
, Z1 y+ l$ }- J9 c0 Y4 c - perror("read");7 _; Y7 b q; j* y
- break;
+ x X# x2 O ~& f; ^ - }//执行while循环读取数据,当
8 e" b2 k, K- O; Q! } - else if (0 == ret)
! K7 G7 `5 C8 a - {
7 L2 d' C2 V; E8 E* E; t - printf("write close!% t7 |8 n+ P9 s, Z
- ");
* M2 S7 o! C3 W7 W. N' M - break;( i9 N# `9 h# h1 _* e2 p# @# B: j1 q
- }
, L3 |1 j/ b9 h& @5 e& T - printf("recv: ");
* K6 W8 K5 A4 o% Y( ~ - fputs(buf, stdout);//打印接收到的数据2 b% d9 O- ]1 t ?/ k: @2 R4 d) R
- }5 d. E. |$ Q2 I1 Y. ]" ~
- close(sockfd);//关闭套接字: D. R$ V6 K1 S" `: h3 |( R* w6 h
- close(connfd);//断开连接0 _9 k: i" K# ~# O, X
- return 0;
/ q- G' J" i9 i) w l7 A0 } - }
复制代码 1 M( D8 u9 h: E; u8 R1 d0 `
% d$ Q Z: r2 j. r% [
- /*客户端*/(具体功能和服务器一样,所以不再加注释)" O: A. G: Y2 @/ ]
- #include <stdio.h>
6 G: t5 }) F m- `, S; n+ y - #include <string.h>
, a" O4 I# O5 q' G - #include <stdlib.h>, {* b$ w. k0 ?3 C) x) s
- #include <strings.h>' {: @) u( f" ]$ B
- #include <sys/types.h>9 t' }+ P( n+ y p+ Q# L3 d
- #include <sys/socket.h>
1 D- v' n7 a+ f( M- I3 W - #include <netinet/in.h>
4 m9 e' j" L7 \ - #include <arpa/inet.h>$ E# P# I$ ^9 y Q. x& \$ D, w, w! Q9 Z
- int main()
' o" D" ^+ f- Z- r! v( x+ P" G - {
1 O0 q0 O- N4 [' m - int sockfd;
$ X4 b7 z1 Z/ @6 d - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) a. Z0 \$ w7 m8 V- j6 N$ n
- {
. w T6 b, H& N( \+ H0 o* K - perror("socket");4 a5 t: I3 E1 R4 B1 Z+ f
- return -1;* L6 k$ f7 Y3 t9 e( f& \
- }
" ]! w6 w1 q* ?& ~3 G8 j! `9 c - printf("socket.........../ r( J$ }% D+ Q% q! I
- ");* z# t! A" _" x9 A
-
9 _# q5 a% G$ T. j7 Q - struct sockaddr_in srv_addr;# y0 J. @5 J7 T% S# ]6 D# D0 ~
- memset(&srv_addr, 0, sizeof(srv_addr));( P( ^# m2 T$ O4 a: z: C* T
- srv_addr.sin_family = AF_INET;+ j* x3 D7 b# Q$ |+ F/ ?+ F
- srv_addr.sin_port = htons(8888);
7 q" O5 u: [& m% W4 D - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
3 L, @. h j" e4 a( V4 ]" }4 c - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
4 r; l6 X8 l5 m) t - {% X o( T2 w$ ^# D4 ]; @
- perror("connect");3 P& X! w- o( q" a
- return -1; //exit //pthread_exit
; ]4 M4 h# y: Z& } - }
0 d; O* w0 h1 M/ i) W - printf("connect.............. A+ Y; R0 G$ }/ L) F: O% O/ H9 _
- ");
* @# |* h1 I R$ E - char buf[100];$ { P2 d+ b) h( K1 [+ j, Y7 V
- int ret;
0 z. n3 Y* {: B5 W - while (1)
9 \. H3 Z& d( A1 Y" r) L. @ - {4 \0 M8 i6 v* b/ e
- printf("send: ");
; Y- Y' T' s8 m, N - fgets(buf, sizeof(buf), stdin);
6 C4 T% `* k3 I5 S& a$ t, G9 P9 X - ret = write(sockfd, buf, sizeof(buf));# @3 R2 ]# r) ^( f: h; ?1 D, U$ Q3 l
- if (ret < 0)
9 d- d6 U( k' {. H, e; U) \( v: C ~: n - {7 ~7 R! L6 z+ ?# U- O; a+ i9 H7 O
- perror("write");8 q$ p0 N+ _% P% [: n' c
- break;
: w3 K8 _' c' S( x- ~3 Z - }2 p3 d9 d/ v+ h
- if (strncmp(buf, "quit", 4) == 0)
$ V; u" T$ q% [* I& O ~ - break;* E" b0 [" R. k! [
- }' R, E5 ?) X j! I/ c1 _9 o
- close(sockfd);
+ R$ }; W+ F0 M - return 0;3 R& z5 h: K) V9 o9 s( w
- }
复制代码 " d# u5 x- T; I# e$ {' `
2 ?/ o3 w! q P) ?+ l, N# t6 ~
|
|