管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
2 R& t D* h0 z( V5 _$ r2 i2 A4 |8 s/ n6 A' ?9 Z
1 |+ @' g" d0 ]) Z3 ^socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。/ _2 f+ ]( C7 J X2 F3 u
& ?! U d( s' S {. @) Y3 q7 _0 x* a( A. t- s7 k0 R
TCP协议) @3 Y* N4 S% {# O* k( p* }: o( e
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。6 ]2 D. t# t# Q2 }
' c" \$ l7 W8 t& [6 f% d; Y
& Z0 o' ^ C G% b! }$ E7 R$ R3 g9 w关键词:三次握手,可靠,基于字节流。
2 _; ?! S |: O" w
( }* t7 `; }/ Z' u9 l; N
+ D, h( Q$ `, W- | n可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
) ~$ o( X& s) m* u2 |
0 `3 w U* p6 y1 NTCP服务器端和客户端的运行流程
( J7 |- t, d3 |0 H
' [1 U' H3 {, w# I6 |( f/ Q$ \$ Q* p# E) [
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?! }7 U$ H# u- P, c# H6 m, O
]$ s7 B" Z9 H
1 L( r' r0 D+ [1 O$ M& w- ~4 m) P1.创建socket" h! |& U' x' Y" \
socket是一个结构体,被创建在内核中1 h) w0 ^* Y4 U3 Z
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
+ Z; _* g8 E% [2 U
2 G$ O4 f/ J7 E G
& F2 S/ L2 U1 L5 w: T" z7 _' y2.调用bind函数9 D6 |$ @! N+ W$ g; s6 K5 N
将socket和地址(包括ip、port)绑定。
( G" C1 x$ u' u5 C+ L q Y3 E 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序7 ~& E; ]1 p6 F% |6 N0 F
struct sockaddr_in myaddr; //地址结构体0 X5 x8 l6 ^# m6 U
bind函数5 r9 F, f; D% e5 l- t7 `3 x5 p c
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% h: j# f$ T* G+ u j6 h5 y& {& ~
- o) K4 T6 @; i2 O
4 E) i O& D! b( J3.listen监听,将接收到的客户端连接放入队列
1 s |$ {+ d& j! `+ O/ v listen(sockfd,8) //第二个参数是队列长度8 L! L0 b4 X& P- S) L& X" ]$ k* ~- u
# p. p; Z1 I) f y( U3 ? [
/ x# d9 _ K! U# w" R1 I( \- n4.调用accept函数,从队列获取请求,返回socket描 述符
0 C0 i, v# n/ a 如果无请求,将会阻塞,直到获得连接
& }9 ]# U; O% y' i, o' h m int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 O/ m9 `0 y* g _9 R5 S, {; M4 v3 w2 ]3 @. Q) g& C! _' h8 u
/ N$ j6 N3 L5 D4 ~4 `1 d9 [
5.调用read/write进行双向通信) d S; e3 Q, |2 u+ L$ i! g& }
3 I6 U2 ?' z# f6 B8 S- H* o. N/ p) ~
7 d# [& B- y/ E' N7 p2 T j6.关闭accept返回的socket
+ X$ m+ P8 X6 u. G. U1 Y* [ close(scokfd);0 E. P+ U9 |4 _2 S1 A
- B/ l+ h1 }6 q2 }* `- O7 ? [6 M! C: @: v
# J% ~; r5 t5 y8 C' \( B' ^0 C$ W
1 i( d! ] h5 Q! a下面放出完整代码* _1 q# Y" D e6 K# t2 R
8 ^; v) \" G# A( u9 ~7 ]; }
- /*服务器*/
" w: Y' T5 h1 ^& Y - #include <stdio.h>3 d1 Q) Q0 s f1 J4 O9 ^( X; y7 L
- #include <string.h>
) m( l: p% `& x3 P, e2 L5 t) G - #include <stdlib.h>" ]" Y* t+ a# ~5 ]
- #include <strings.h>
) ? M0 y8 r( h$ \' l5 q( f - #include <sys/types.h> G! \- t% Z7 o# s8 G1 L
- #include <sys/socket.h>* n! X, J3 n3 U
- #include <arpa/inet.h>9 K2 @2 a, [, c7 @
- #include <netinet/in.h>
8 |. U$ r: \0 V5 ` - int main()
T( ^0 m% g/ ? - {
' m* v: b( |& Q - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- P# M- O4 |$ L3 w
- if (sockfd < 0)
% H8 L5 P% C+ Y7 W - {# L( }" H+ w2 e5 \0 x& t5 p+ p
- perror("socket");
* f" p8 X3 F4 S, o1 ^ - return -1;
, ?: ~3 z! d- ?7 W* N - } //创建失败的错误处理
4 A3 V7 q- k& o5 a0 {( K( { - printf("socket..............1 j/ v V- w3 T2 n0 j& N f$ j( x, Z) B
- "); //成功则打印“socket。。。。”
# {( z0 y7 L: U5 @2 p - : Z4 `1 m% V8 i
- struct sockaddr_in myaddr; //创建“我的地址”结构体
2 w3 t; ~7 y' ^/ U" z# w" E2 `8 x - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)( V. |/ \: r1 ?
- myaddr.sin_family = AF_INET; //选择IPV4地址类型! ] j3 R9 v+ s
- myaddr.sin_port = htons(8888); //选择端口号
; ^/ k$ W/ q9 u. s3 M - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
% W. N( G% Q$ \5 X( w) e6 ]8 W5 o -
$ d' S; X" j5 g4 W" |7 { - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
5 s# Q6 B; n3 d1 X. b' j - {
+ F' j: |: u/ n - perror("bind");
2 x- F K# k4 d/ w' z - return -1;- o' k2 X& m# @# O) p- z
- }
# N# V& G* P4 h - printf("bind..........
2 _; p; s( M4 S0 i; k - ");
+ c$ a1 U. R- l j/ P - ; Q6 m% R- E2 Q9 _' Z3 I+ I+ \% m
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听5 q- x3 d( N( }6 i/ ?1 p
- {
* z% Y- p* |3 X8 r7 C% ` - perror("listen");
9 Z- [! E4 \5 w) m4 [& v - return -1;, b) ]" g0 i* v, o& f: o
- }
" A5 R/ E9 |7 m& }2 C, j - printf("listen............
2 @5 V1 I1 T' }* A - ");
$ S! U Y2 B* B7 X1 b: J2 C -
1 O7 D$ H4 ~& ~1 p/ Z7 T, n. I1 ~ - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求) ^% v0 T1 E$ o4 E$ O0 j" W: t
- if (connfd < 0)6 t" p% t! Q9 `0 g, `0 V- ~. D- C
- {
: Q/ q+ b7 k6 ]; i" F( W - perror("accept");
7 R! }0 t& x# _8 ]! m - return -1;4 X( w3 S9 l/ Z9 N
- }
: l( @0 a1 f; p$ o! |+ u! J - printf("accept..............
! C) L1 w7 ?" |0 `, | - ");
% K$ M5 @2 H8 q) K5 c7 M4 Q2 X: C - char buf[100];//定义一个数组用来存储接收到的数据
" P) l* U; j: Z - int ret;
x. G6 R1 }$ U; p' Q - while (1)# G% u. F% g7 D; U* c$ k
- {
( l) f2 p- R2 j. p - memset(buf, 0, sizeof(buf));- ]& w) L/ Z1 U0 x9 k, L9 I5 L
- ret = read(connfd, buf, sizeof(buf));3 h6 y4 l$ O# u: H6 V, T* W
- if (0 > ret)4 ?3 M! p* a4 g. k0 y" [. o
- {+ q. l5 F& Q) V5 k" T4 K4 u
- perror("read");$ s+ J3 v% z T z' ~* \
- break;
w# L, A4 d/ Z7 c& ^ - }//执行while循环读取数据,当( `" }) Z% @5 }/ X) G% J
- else if (0 == ret)
# Z9 m0 ~3 Y5 o: S - {
9 p8 F% t9 W1 R2 s# s$ e/ u" k - printf("write close!
6 o* `" }% P4 I% r' D - ");- v$ K+ Z1 N$ L2 L x, _& m
- break;
7 K& S ~' z. ~% M - } Y h& f& S: }# C
- printf("recv: ");+ `4 ^2 E# s5 }
- fputs(buf, stdout);//打印接收到的数据
% a9 R( }( }: F4 i - }
* k- i; j3 \( h4 d7 ?$ P8 U - close(sockfd);//关闭套接字/ \5 V" Q" }6 L. `
- close(connfd);//断开连接
% {7 l/ ~# X( E0 a8 A% y8 E. J - return 0;( T+ D6 I% ~+ s1 p( ~
- }
复制代码
! O( N' v3 G% y. I+ R9 C, A8 U' S. F. a) r( ^
- /*客户端*/(具体功能和服务器一样,所以不再加注释)" X4 t: X- g" ?! {+ K7 Z9 Y
- #include <stdio.h>+ i$ M: V w& S' b) R
- #include <string.h>
2 r# @: z* j1 E - #include <stdlib.h>* E3 ~' W h2 u4 N6 V$ s
- #include <strings.h>
, ^+ I3 K* v d" X - #include <sys/types.h>
% K. ^- _# a5 | - #include <sys/socket.h>
* x/ g9 O! l9 S# Y' } - #include <netinet/in.h>
. ~; L% v! e8 G - #include <arpa/inet.h>
4 K/ {/ k7 `' j* W/ Z+ f5 `+ | - int main()
$ ]1 e5 m5 B/ s* _+ F" m - {
z ], l- c+ T# {0 w- H6 F# o! b - int sockfd;6 \1 Z0 C! s+ u% O& x% d/ z( W
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
) d1 t3 ^- h0 F2 q( d8 \. a) }; N - {! L S$ o8 ?/ ~ ]+ v/ ]" H [/ \
- perror("socket");
; j) ^+ t0 l1 s - return -1;
5 ]$ z$ K, A0 _, p - }7 i0 U* \: B' O
- printf("socket...........& v! Q2 r: h% T
- ");
4 e. z& S3 e" Y y -
3 s" m4 E; [. s1 |, c1 p0 c - struct sockaddr_in srv_addr;
5 |9 g! K. [7 b( `" w - memset(&srv_addr, 0, sizeof(srv_addr));+ w) {8 S* {6 L& Z/ O" Q1 V" v) s
- srv_addr.sin_family = AF_INET;
; n' I7 }& c* A! S - srv_addr.sin_port = htons(8888); L6 x! G" B4 h) o1 Z
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
: w) w5 [+ W; E5 o! @* d( F - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
: r* {* g$ ] A+ \4 J2 A1 a - {
+ k \) \; U) y0 D; J7 S/ U - perror("connect");
, ~( w6 S" |$ n2 D; i0 K, Z - return -1; //exit //pthread_exit
7 | t/ a# G2 i7 N' j$ ]1 o7 O - }- [9 o' X" m2 _; @/ r
- printf("connect..............
9 P7 @5 C5 s$ j* S - ");+ ?3 b |0 W7 h) V: {. C* Q
- char buf[100];
' N4 R" |! G; I9 K: p0 `' p+ i3 m# K* J: ^ - int ret;
. M a+ [2 h1 X J; y - while (1)
9 R8 R* d, O; H# |# B' L8 B7 \& I - {- ?4 n/ @! Y- i# K
- printf("send: ");
- g& Q2 S$ w, {/ }* \ - fgets(buf, sizeof(buf), stdin);
; D7 Q; [! W5 ^$ I9 s5 ` - ret = write(sockfd, buf, sizeof(buf));4 e8 y: n5 q) ^& {0 X9 I/ Q0 g( o
- if (ret < 0)7 r' k5 r- s- }
- {5 ?8 Q7 |; J5 E& \+ U5 B
- perror("write");
7 s6 {% Q. h; u' C/ r - break;+ U( z- `) l0 b+ @
- }
1 i4 b' u' l3 O L - if (strncmp(buf, "quit", 4) == 0)4 r1 k1 v7 u- Z) G+ |0 j" S) ]0 [# P
- break;
: n. T+ M( S# c( y - }
& D u1 P6 G( T& V - close(sockfd);
6 P( p. d( q+ `. q - return 0;
2 |6 C# A4 P& K5 p - }
复制代码
0 t( T2 \4 ]& \6 A1 r4 w! x$ Q6 @% t( G
u! W1 U3 b) D |
|