管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。: ~7 A8 M5 v& t# K
& T; d# H4 G) m; s1 D2 E6 e+ U8 p: `
- u8 E- X: O& m5 Esocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
1 G3 ]$ _) k- c& l9 ^' n7 A& ~7 q' q- _! V9 H ^
: Q6 I+ E% u: c( C3 r" \3 `! v5 n
TCP协议" U* a5 [3 O3 U6 [9 L+ Z+ N6 C
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。+ X7 V) j& i& j5 u8 Y1 i
: G/ {8 ^2 l0 [$ u: q9 B
( o5 i4 g0 H% p0 `1 C关键词:三次握手,可靠,基于字节流。
( R" h8 d2 P/ w4 c
& j0 G# y. [% a/ v& A4 ~7 \; ^0 h/ }
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。+ Q8 z! q& {* j( l
' x2 {8 I4 Z8 ~5 STCP服务器端和客户端的运行流程
! X6 b1 p6 C7 V/ f* h; u7 _* O1 h. I. J/ {( t1 N i( D# _
; U) o9 X f; j! {/ B( W* H
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
3 ?- _. G0 I; o
# g5 S% p/ g- ~" L3 T6 M. c$ E) C9 _* l/ N4 |$ {! S! ~: W- \, ^
1.创建socket
5 O/ f, D" Z/ p5 ~: w0 ?. P/ f# W; O% ~ socket是一个结构体,被创建在内核中, T3 t" h9 k @# H Q5 m8 W
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
3 n1 P# Z! P, K* V. z
a# R! e' A3 V6 J. `8 B
' p& @* M' R v. D2 V; z& ~2.调用bind函数
, u# G$ k5 M5 T. u% z; J 将socket和地址(包括ip、port)绑定。( Z" p& q2 m/ v3 x1 O
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序0 l; B' ?; `! U2 c# @8 k6 j
struct sockaddr_in myaddr; //地址结构体# g- n* S0 E# `( B- ?8 R" D' H) N# M
bind函数
' _, {4 g% z, Y% r bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* {! m6 j; Z+ e! I4 d( F# v% @' g+ {/ X
4 t! R: ]- M2 A* v3 x5 Z
3.listen监听,将接收到的客户端连接放入队列7 d$ l2 }+ E6 G9 G9 i
listen(sockfd,8) //第二个参数是队列长度! K7 O2 b& ~( U6 ~* j
% S! E7 w- L; X% W( ]4 o% o1 @" v. T+ s) `9 J3 M) e) o% y9 n
4.调用accept函数,从队列获取请求,返回socket描 述符
5 Y6 G8 G4 Z: N x' ]8 ]5 ? 如果无请求,将会阻塞,直到获得连接
7 I6 |+ K& N0 H2 e3 }) q0 j" o int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
' i3 C, R3 S; m% u O; F S* V: {# Q/ R' M& ~
' v6 b5 o) _; T v: ^
5.调用read/write进行双向通信
, z! C2 A1 i! ?" A- Z8 }' D) [" N5 D3 ]) |! _
, n7 y; J& h4 z' C6.关闭accept返回的socket
?; W4 m# M. P+ }5 Z) x1 } close(scokfd);
9 @0 O X, {+ T& E7 a' p) c4 b# ~" k8 J: Z9 T3 s8 |
: `- h4 q- F% Y
* [7 e" ?& }: ~ [$ N* l
# Y5 o( W$ p6 ]1 U下面放出完整代码1 Q1 ^8 `; t) v# O* F
% N- m6 P) [) T& T- /*服务器*/& \' M/ S( T% R3 O
- #include <stdio.h>
* N7 O( U; \1 ~; o9 W+ z' [ - #include <string.h>
4 _6 ~" M( M/ K/ o- J - #include <stdlib.h>
6 S, ^. z% u' t8 C$ e9 v - #include <strings.h>
+ B' ]! {9 y: u& E( D% y - #include <sys/types.h>
' v3 |1 z* A: Q9 K4 a - #include <sys/socket.h>
z! u; Y1 K: A! x3 N$ b6 w - #include <arpa/inet.h>
: X& _& ]$ A& V" i. h# u - #include <netinet/in.h>
3 y7 h) W2 a+ |; T - int main(); X! m* C2 m5 b4 d) f8 _
- {
8 u$ v3 m) Z2 ~$ X - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字$ K$ T4 \1 l% |
- if (sockfd < 0)4 {* h3 z% Z$ G2 b# ?
- {) C" A0 P- {* {9 x8 y& s/ ~* P8 w
- perror("socket");8 d! T& R% T1 o2 @) e
- return -1;
* J0 u& O6 n! J) k" s- }0 s+ z% g# g - } //创建失败的错误处理
) N N0 u/ O, R1 T6 T - printf("socket..............
! ?" c2 b1 C# K u- D/ H - "); //成功则打印“socket。。。。”
W: @8 _" \1 n, ^ - 4 c& S1 |5 D+ g: ?" k- r
- struct sockaddr_in myaddr; //创建“我的地址”结构体% z5 U; b7 ]* K
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)+ q9 C" W* Q0 q) ~2 b; `# h& i
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
/ d1 d/ E% D7 d - myaddr.sin_port = htons(8888); //选择端口号" N# @/ v+ G5 q3 D6 ?
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址9 R' R( `" K: j; @
- 1 ~; W2 x8 h- a% p+ y% H+ b
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
* s% L" Q1 m, l8 j1 L - {8 ^' O* U5 X N( J; k T2 E5 u. w
- perror("bind");
$ I. }6 e" |4 |$ V - return -1;8 _! Z2 P _$ z: K$ F* |, R& n
- }' }9 w1 v1 y! i+ u
- printf("bind.......... b; A1 s" W( V$ V! V) l# Q3 i
- ");' p, ], h+ c4 c z2 Z$ Z3 C" j
-
7 ]3 {2 u, o) V3 U$ U - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听' R/ y) S0 b5 N1 I
- {* ]! p: u0 e9 h. r4 `
- perror("listen");
: n* o% I8 U+ g3 H( I% o6 p - return -1;( s8 o# O+ ^! O' s% W }2 k; m
- }
0 Y2 z5 N0 Z' F' R' X - printf("listen............
! J* t# N# c2 j, |7 S5 A5 @ - ");
3 R4 H6 d: c1 n$ B6 ]# [ - 1 m% ?" n T2 z. w8 o
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
* R! _5 N2 L" x2 _ - if (connfd < 0)
# H5 W0 N; w9 c) D# ~- |; j' G - {; m- `( y1 J8 J7 R# }2 [
- perror("accept");, }( L. Y o5 }
- return -1;0 Y' g: y% u1 M: j. r$ b( r) E
- }+ p: V: ~$ _8 O
- printf("accept..............5 `3 G/ d0 t$ _2 W% P- Y# J
- ");
/ Q. \5 O- T! c, s: k7 X - char buf[100];//定义一个数组用来存储接收到的数据
$ `1 Y* n, C7 R% ]9 j/ h - int ret;
4 @# L) E7 Y5 Q: I2 N! K3 j$ x - while (1)1 N; j+ r0 O- g
- {
/ D) c4 }, ?, u _; S - memset(buf, 0, sizeof(buf));
: d! ^, t$ I# y; |$ A7 F - ret = read(connfd, buf, sizeof(buf)); ?& x( @2 `0 p: p( {6 x
- if (0 > ret)! e s: \3 ?4 l4 U, T- Q) V8 A
- {
( ^7 J* ]$ T( {- q4 h9 b' L - perror("read");
9 G: @! M1 l4 q" m/ Z - break;
: \* J! i+ f8 K: W P - }//执行while循环读取数据,当
3 \! I" n( O( h# ?' \ - else if (0 == ret)
4 n; Y v \4 q/ w - {1 z% e6 c: I5 i* d- N; F% r, S6 ?
- printf("write close!
9 S8 G( {5 u; H8 Y. F! l' G - ");
3 B( q; a2 G0 C9 [- r - break;6 T4 n- S3 D* {( D
- }* O" U( B2 N! N& [% D" \0 |3 b
- printf("recv: ");
- v5 P. V. T! ] }6 t4 @2 N0 l1 r, k+ c - fputs(buf, stdout);//打印接收到的数据$ }- g9 c; J/ Q
- }
/ M. I# ~ }5 x. \" \* k - close(sockfd);//关闭套接字
1 @9 v1 u7 h# R/ ~) U6 e - close(connfd);//断开连接
8 ~$ P2 M+ |6 M+ b% w& J- @5 O7 S - return 0;" g+ Y) @8 P8 f1 a
- }
复制代码 0 f D% a3 b7 k9 E
8 Y! |5 K" @ e, r- }- /*客户端*/(具体功能和服务器一样,所以不再加注释)
O2 r( d6 D* P& [& }# _# a& t8 u - #include <stdio.h>& a0 B! u; O6 N6 ?
- #include <string.h>
" ]6 R+ G! E! t( @' z3 R5 U- K - #include <stdlib.h>2 @- e% S* W/ l/ |1 K! u4 x
- #include <strings.h>/ e/ b# Q, ]( s7 h, z+ G* C
- #include <sys/types.h>: S2 N7 k) x, g% \ C* f" X {
- #include <sys/socket.h>
3 I* j7 a% j3 y5 y - #include <netinet/in.h>
/ o5 I6 g% i: {" n$ }- I0 X) r - #include <arpa/inet.h>* G5 W& v# k( m( p+ f$ e
- int main()
3 |8 A0 F$ Z: H - {
& ^% ]: N$ a! Y- [ - int sockfd;
& f3 ~5 {+ K0 m, X - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- T' ]& k$ _* f/ k8 Q' h
- {. R4 l# p3 ]+ w, t
- perror("socket");5 c; G1 k& |# i# w/ U/ w
- return -1;! l1 A! y. p' P# R
- }9 V3 ^+ P/ f+ ]+ U
- printf("socket...........
7 }8 t5 ^( [* i1 y& \( w9 C2 c - ");
O' m- X# [; _ I' d5 ~" j -
. m; x# D- t# _1 @$ \ - struct sockaddr_in srv_addr;
G" v: Z" H( E) b% q W - memset(&srv_addr, 0, sizeof(srv_addr));
! B9 O+ s7 X K - srv_addr.sin_family = AF_INET;6 q' |% k: P* V8 }5 R3 k+ B
- srv_addr.sin_port = htons(8888);
4 B+ h% i: ~/ V' r2 j - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");% Q7 q+ x4 Y" n' d; v
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))" q' T& S$ L# m, R
- {. S2 B0 G+ ]* ^ e
- perror("connect");
- X; K* Q$ a P( G6 z, S - return -1; //exit //pthread_exit
/ M* _* l8 F2 U- n+ c! \! K I - }
, ?0 P! g ^ y1 } - printf("connect..............; Z. A. E5 y0 M
- ");
% y1 `5 X4 D5 ?# o- e7 u2 c - char buf[100]; M- G3 [) H6 e5 J0 u
- int ret;
/ Q8 ?* [+ u8 v8 S9 [: a/ R- J) t& X - while (1)
: H& q9 v+ c' B' ^ - {( ?! t* {% [1 S. K v
- printf("send: ");9 Q. | v2 p. t4 _& F2 D
- fgets(buf, sizeof(buf), stdin);
d) [. S2 e! A* S2 m: a( q3 M3 r2 K - ret = write(sockfd, buf, sizeof(buf));
( U& D; V$ J6 Z3 s9 M# S - if (ret < 0)
' I8 n, f+ {( b- P0 e# a3 s# J; u - {
8 K: @" S. r# a8 t - perror("write");+ H7 B# J1 J4 v- y" o2 V
- break;% Q4 {: g/ X( T4 z$ Y
- }
. r; T7 }6 H6 P: _ - if (strncmp(buf, "quit", 4) == 0)
, ?& t; L& j5 e" W8 p1 n - break;" d* B- H+ {7 c" S
- }
" e) b9 `, \: K% V+ m$ g& p, G( ~ - close(sockfd); Y$ R2 w& _/ c- |: E% S/ u+ ~% f
- return 0;7 H7 g7 Q6 W/ Q
- }
复制代码
' \' t8 b- e& @. k! ^4 L% ] f1 u
|
|