管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
2 P! B# C: V9 C. E; \3 ] }0 B9 e; T% C3 |$ x2 g
( ^; o( g6 S+ b4 t9 Isocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。9 q8 O6 i0 G V* X$ r8 m n
9 I D6 t5 Y3 u" j) H0 ]# t: w, f g0 J3 y$ u
TCP协议) _9 t6 F1 Q z8 |$ {- A" `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。6 Z7 r! _9 B/ b' U! ?
# e) G% ~* j7 Z8 [1 M$ J
5 Y2 p! g4 m0 e7 ^关键词:三次握手,可靠,基于字节流。
8 Q( v8 ?: H2 @$ k3 i: s/ H; {. F. @' I! I6 D
1 e" Q) f! v7 s0 h& F: g可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 s6 `, I* V8 J7 P+ n5 |
7 G& ?- }" a( K' E) R# l5 g5 {
TCP服务器端和客户端的运行流程
5 |" q5 g( j, F' K2 N7 Y2 p# z6 b- `: P$ ~
2 b1 N6 ?1 h/ N* _6 g# C S1 F/ G如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢? l# t- v. _/ {& p @% M3 }# j' ~
4 t9 p0 \' I& s$ r4 Z* Y4 P
4 K' M( C+ A6 m# V
1.创建socket
: p0 D- O% h: P+ j1 f3 _5 T socket是一个结构体,被创建在内核中
% Z O. J1 u5 I" E' r* L$ l sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
$ s5 u J' f) {! P# _6 A1 t: Z, O0 P' S! r# K% y$ ~1 ^. l- K& B
$ a7 y" O: H2 a+ Q
2.调用bind函数
# z1 g l( V x+ ]6 I/ Y" m" Q 将socket和地址(包括ip、port)绑定。
+ v, Y! r% l$ v8 h* L 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序$ r( w; M) W/ {: j- W) [
struct sockaddr_in myaddr; //地址结构体
, F; H, P2 T8 C& i4 @ bind函数
5 j! {, U- C7 W$ I bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
, Z; A, V& b5 E4 ~0 L. j8 l
: ]5 e% b* A2 a- ?% j( g0 F. e# }: u1 g1 D7 K
3.listen监听,将接收到的客户端连接放入队列
4 A; _* z s1 B* A listen(sockfd,8) //第二个参数是队列长度
* e+ ^/ y) [7 O7 m O) V5 O% ]4 M+ ^% X1 I
# m+ G1 N# I( |3 S8 n) r5 U' A) ^4.调用accept函数,从队列获取请求,返回socket描 述符
5 ]% W a( S) Z! h 如果无请求,将会阻塞,直到获得连接* A0 a4 P+ ]5 O
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数" p/ T+ \6 l3 m# w7 F
% h! k6 J4 h6 A2 L; H
' g; J7 Y% a/ C; k( k: w
5.调用read/write进行双向通信6 k) b2 C# R4 ]+ @0 v2 {
/ E7 a0 G- }0 o) Q9 I
& b: Q, A' A4 r: z. z2 l6.关闭accept返回的socket
5 b5 I" J9 [& B6 E" r, g close(scokfd); v' @0 i- L* y' T j6 |
2 }0 A1 U+ y: X9 y. T
+ u' F* p) ^2 o) a5 o# N9 x# F: v9 R2 X/ U
9 h5 `. _! |( {! C5 N
下面放出完整代码1 ^2 A, r3 |& S2 W: Z2 p6 J ^
9 L& t! P- Y, ?4 w& W0 v- /*服务器*/$ g3 S' Y% L* z* N X7 `
- #include <stdio.h>
1 I# e4 _+ ~4 r; J+ z$ q - #include <string.h>- e4 w' |) Y2 P" f/ i+ X
- #include <stdlib.h>
5 s$ U2 ]8 z2 a- r" ^4 p - #include <strings.h>
+ N1 M6 V1 C( b! n( M$ | - #include <sys/types.h>
: @. c; u7 I9 Q; {. H5 Y - #include <sys/socket.h>- D# i1 H- H% C2 b) B
- #include <arpa/inet.h># Q0 ?2 \, T; z7 X
- #include <netinet/in.h>
: [3 y+ l3 {+ b, q - int main()
1 \9 n0 E, m$ L& G7 @2 } - {
. H! s. ^0 E D - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
8 U9 S" G. H/ t- o# Y6 A1 n8 h - if (sockfd < 0)8 U9 V+ r, j" ~/ m3 G
- {' Q+ ?1 i/ G* e9 R( X
- perror("socket");
1 R+ q2 K' s. s8 q G+ v0 _ - return -1;
4 [" c+ `3 I: O5 q; A$ u1 I6 a - } //创建失败的错误处理
: n! M; Q: \, ^: m" K: f4 o: s, q$ c" z - printf("socket..............
; ^. ?' w7 g% c0 P - "); //成功则打印“socket。。。。”0 G6 ?" f$ ]" _) O
-
1 E4 ]6 d. D8 v5 s: b5 E; B$ w - struct sockaddr_in myaddr; //创建“我的地址”结构体" p* L @% M- O4 a9 b% y7 r' k/ z
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见) t, G" W0 k' n1 x1 D
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
% q. q* U5 e- l6 x# ]9 w% i - myaddr.sin_port = htons(8888); //选择端口号# v& H; L9 s+ g) _0 f
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
0 v, X d, y8 j9 L* A& U -
# L: q/ z }( I. r5 g% B a; J - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字: H! t$ ~$ y) X
- {
3 ?6 h7 n( h( j" s; n7 I - perror("bind");
, g& Y2 ]7 {* a2 o r - return -1;
9 ^9 T/ q3 M7 p6 v, W - }5 i0 g- M6 b7 S* u2 S% f! g
- printf("bind..........
1 s0 E9 q. G, ~ \ V - ");
, B, R: j. j0 e {: _! F -
' ]- L4 p3 [! w: B B* s) @ - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
; q" Y1 [* y) M8 C& b' [ - {. O, w+ X7 U: J$ l# l9 ~
- perror("listen");& p6 H: _4 i( J% t& O: \% y% W
- return -1;- q' @! x. F/ l: \: k
- }
- p# j1 ]; O$ h. o3 }: F9 v( i. S - printf("listen............
& V7 c: E9 S) w o$ Y - ");
" V5 y A; @) @4 I; b -
5 g$ {4 t& |, {# `& D% g8 \4 Y - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
) ?5 C- x8 j/ Z - if (connfd < 0)
, U: ?7 B+ a3 i5 ~ - {$ C8 @# g6 Y% `: K' M% N2 o
- perror("accept");8 k+ m' N O1 o/ }- B
- return -1;
& a- Q- `. W ], p6 f - }/ k# H/ M" [$ _* \9 k0 _* _% q7 s
- printf("accept..............
* ^" ^. O5 Z) y! W* A* K$ g: b d+ Q - ");
! ~$ b, H+ @8 j9 p. B - char buf[100];//定义一个数组用来存储接收到的数据
# U; [2 f! S* t7 d6 d' B - int ret;" o" R6 @* U3 M
- while (1)
* U# w9 z) l) i! h9 q - {
+ T# X# p6 X1 o& m o z - memset(buf, 0, sizeof(buf));
# f0 T% j' T: \* S - ret = read(connfd, buf, sizeof(buf));
, u2 n4 V1 _1 J) B( f' c: b+ R/ L - if (0 > ret)1 F% ~2 l$ J/ i. Q; V1 V
- {, M9 e* n( P5 ~. @# I
- perror("read");
6 v) e# A1 K3 A; I3 O - break;8 G6 W( c0 o+ J# D$ r6 a
- }//执行while循环读取数据,当, [, ?2 \" ~* R: C* q% p1 ~
- else if (0 == ret)4 z' D# d/ W, b5 H9 @
- {; ~! A ^1 ?* Q, u# n
- printf("write close!
: p6 ?7 A4 ?8 A/ e - ");' A0 g" z3 L# f" i
- break;
6 E: ^9 O8 K& d& f$ `9 S4 w5 q3 b - }4 ]9 C% `0 b0 \! ]1 v
- printf("recv: ");
# C& w( ?9 K: j7 A- R& r - fputs(buf, stdout);//打印接收到的数据2 m# ^. Z; P2 n3 N) y! m7 ?5 k
- }
# C3 O' y/ z; x$ v U! N h - close(sockfd);//关闭套接字; G( v; q* V: @3 ~
- close(connfd);//断开连接
9 o9 U0 p8 N4 i8 }. ?/ K& r: B - return 0;& l( Q$ ^' J& O9 M+ ^
- }
复制代码
& K$ E9 G% O W/ y$ ^ s
. e7 B3 F$ j6 O- /*客户端*/(具体功能和服务器一样,所以不再加注释)) I0 _- h' Y8 ?4 }& n
- #include <stdio.h>
- n6 l+ Y; F) @, N& y) ? - #include <string.h>
1 |3 `# ~1 D1 V1 P* J - #include <stdlib.h>2 F+ \8 q' _% O7 k
- #include <strings.h>1 O1 ?& [% e( d6 l
- #include <sys/types.h>. ?; O4 u* r0 }" v' |
- #include <sys/socket.h>
" o: i) @, A* V; a - #include <netinet/in.h>
/ p/ L; W+ V+ D6 b! V - #include <arpa/inet.h>& h M, d' b& G9 M: S
- int main()
* N' [! |; `" E5 {5 N/ n - {
; \4 h" ]8 V* t0 G. t$ W' c - int sockfd;
4 k9 b z8 x7 [ - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
8 Q; Z! m) [) O9 x - {
m2 q4 n1 N8 j; T9 ~9 S, l6 H - perror("socket");
' [/ o8 u9 ~" ] - return -1;
9 c% A5 g1 d; t+ b - }
& d3 G b+ _5 H7 n) l - printf("socket...........
% h( C+ i$ @1 Z' |( u; a - ");
( \( W# o. W5 T |6 l -
3 ^: `) S( \* l. g5 J - struct sockaddr_in srv_addr;& S' b6 f/ ]9 [$ t) H5 p' O
- memset(&srv_addr, 0, sizeof(srv_addr));
5 l2 b+ Q9 Q, Y, ^ - srv_addr.sin_family = AF_INET;' X0 J: C& P: f) c. s
- srv_addr.sin_port = htons(8888);
' {5 o t% p# L, N9 Z - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169"); f4 v. ^% Y1 P) [- z8 ~# h
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
9 S- D, c( X% n+ b - {
+ A. L9 `, N+ q1 l: e% g6 Q2 |1 P* d - perror("connect");- \) r4 c R2 {- B; f- O$ r V
- return -1; //exit //pthread_exit
/ m# t9 q* `8 e2 _6 n - }. A" Z( k; K! X1 t2 L3 n
- printf("connect..............
$ ^. O- G9 d: J- Q6 \' d - ");
' V2 n0 S2 f/ m - char buf[100]; m$ a- ^2 v. M6 _2 @5 S& w
- int ret;; ], @. C$ w( {9 a3 b6 y: p
- while (1)& @: ^0 ~$ H) ]
- {
4 f( l$ U6 y) _2 K) r* c, J - printf("send: ");
. \0 V! {/ E v4 `6 x/ F - fgets(buf, sizeof(buf), stdin);
, `4 h# B( w9 w1 ^; |3 {1 A6 W - ret = write(sockfd, buf, sizeof(buf));
3 D& ~: v" k7 n4 U - if (ret < 0)
/ F! v D# D8 v$ {7 R9 E - {. A# [1 w2 l8 O" H
- perror("write");
8 E" J4 x v5 g. o: H7 A - break;
8 {* U! c) e7 E" ~ - }
' K+ C, G5 R6 U - if (strncmp(buf, "quit", 4) == 0)
* r6 K) B# \' K - break;
9 {+ @) |$ h% u$ `6 s2 H0 o - }
! @, t, B2 x3 d( B: n - close(sockfd);
- d" O5 B1 a% X - return 0;
" g# ~& ]$ @8 l0 H - }
复制代码 : i+ K) I8 p& K* B( E
b- J; w* V- y4 ^$ Y; c+ _5 J |
|