管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
9 d) Z6 @+ x% }" k3 n8 J* ~. ?8 ]) Y8 Q
+ A" G) ]7 n' n3 s7 c' N2 ]. U; l
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
' e3 ~2 @- B' @ V; f5 ~! l7 t0 |
* D: W; ~4 B1 y6 S1 C- k, O8 r2 L: g5 [; t4 l4 k; l1 m
TCP协议2 P. p6 ~3 b; H+ |8 ^9 r
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- T, z9 `% n0 R# W
: W+ v5 ~7 w% D) b3 w
/ K5 _- s9 u% `# j9 @! J+ T关键词:三次握手,可靠,基于字节流。) O- y! d/ u; V" P# I
' q/ ~* N \: ^1 m$ c) N4 L( e6 v; G
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
0 l% _! h% O" R7 _; i! ^* l
# u( J9 @& F, T/ V0 Y+ g3 H3 J* ?2 L
TCP服务器端和客户端的运行流程
" I5 c; d$ W- H3 C* m/ I+ G" N/ K5 B' A9 a2 W7 ~8 D
X# K) s; }9 Z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?# S' m2 V4 x) k# h( a$ V$ t
; H3 @0 x% X9 v7 H) ^9 \9 @9 Y, b1 x3 ~/ x! p* O8 I' P1 E
1.创建socket6 ^4 _) v& M& u4 u1 v0 H& r' x
socket是一个结构体,被创建在内核中3 R. P) ^" b. L- H3 Q9 w
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 y# c" L1 I- b8 u/ y) D& R \) c& u% B9 T) `$ [* f
( V+ ~' u* } |: |. } {
2.调用bind函数% p; b8 V4 \( s; ~0 e
将socket和地址(包括ip、port)绑定。8 u0 g7 R1 E8 H& ^: D
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
# f( b( R: X7 [# M" T/ ^# c( ~ struct sockaddr_in myaddr; //地址结构体
1 G/ _/ h4 Z1 U! d/ t* O* f bind函数: t) j6 f$ Z" n/ p' Y2 t
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
! o9 ^) x! l7 S' Y: H. ]0 G1 ^% ]- a7 L1 m, p
* h+ S8 R' @! @7 n% T8 |) X
3.listen监听,将接收到的客户端连接放入队列
0 t; f2 q! N8 [& V/ F listen(sockfd,8) //第二个参数是队列长度
; I! h# ~" q: e" W' d( R
A7 Y E- @; M; M6 J; p9 ^; I: t# \8 R9 K0 u
4.调用accept函数,从队列获取请求,返回socket描 述符
+ Y# |3 h2 {. R8 r9 }2 e# ? 如果无请求,将会阻塞,直到获得连接
) W8 V& L( f9 H) g: k int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
/ R3 s- [2 c. W K: h: F
S2 W' A' {" p1 W3 `& {
, m3 @% y! j1 R/ I' G# M& s5.调用read/write进行双向通信
" f1 H2 {" ~3 y* f- ~3 F( w
& |' {* m) S2 J" Z e. `" t$ E0 N; ?+ Z& A# ~
6.关闭accept返回的socket
8 H; x/ j9 m+ ~% D close(scokfd);
6 Z2 E9 V2 @3 |% X, `
' P5 b& u) {; }& ~1 }
+ \' Z2 R1 |6 k- |; w
1 w+ | @' e) K# W* F, u% C* W$ }8 r' h! c& V
下面放出完整代码
- z- U9 D/ _% s! G& I4 K" @% x5 ], Z# r! `- w0 g# M
- /*服务器*/
6 {* P' g" z, A T q - #include <stdio.h>
$ g9 v; i' x2 Y! { - #include <string.h>
, D& J5 ?( n0 d" C- w - #include <stdlib.h>
. V9 v; {* O( D4 ~& h0 m - #include <strings.h>: Q# S# Q3 ?5 N" [% h# p+ M
- #include <sys/types.h>) f, P9 W3 j" j
- #include <sys/socket.h>
( h' r! x7 G! u8 _: g - #include <arpa/inet.h>: }0 w8 ~6 [3 v$ t1 k3 d4 @
- #include <netinet/in.h>
, g: ]. p! h2 o$ ~0 J; |2 r - int main()! L9 ]1 J/ p/ }$ [- M
- {
' @+ \0 i3 T0 B5 U) b9 x - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
: e( @/ o; y; y* t' R9 h - if (sockfd < 0)
2 M1 `6 |) \! q* O7 Z6 q3 o - {. B' o8 L+ r7 g3 B( u' R
- perror("socket");
5 S+ t) X' c0 k - return -1;$ A6 _+ i O) z
- } //创建失败的错误处理
; a8 `/ C; @9 u9 u% H/ k4 F - printf("socket..............
/ ]- ?+ f/ t8 Z, J7 j/ x) X, l - "); //成功则打印“socket。。。。”9 P( r. E% `: M7 @# L7 x
-
& k& p6 M2 Y* f) d3 G - struct sockaddr_in myaddr; //创建“我的地址”结构体0 Q/ ?; x) W, ^% }/ v
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)8 n+ b7 d3 b8 K8 F
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
' ?8 T2 V' z7 o: o. F9 S7 }+ z$ z - myaddr.sin_port = htons(8888); //选择端口号$ A8 R1 _) @% v# r
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址# j8 ^. \3 x% S! J
- * R) k1 b) w, x4 o& d* O0 Q0 y* K
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 X" d6 V) b6 p# l4 a* x$ P
- {
/ C6 f# ^! D0 z. |; G' |8 }- { - perror("bind");* ]1 D7 ~) x0 H6 Q: r
- return -1;" b" f4 b- Q( S% K4 R7 B4 H* e4 N. ~& b
- }
; s* l% g( h' U - printf("bind..........8 R6 J; y6 Z6 J( Z% q
- ");( F) {* w+ \- t. Y% ], T0 s& T/ O
- M( {- N |0 d( n! W j4 n7 V# L
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
3 @5 B% \5 V) y( t- c# N - {1 Y6 n. k, w( L+ A# q S: B
- perror("listen");
* c9 g3 ]! S* g% W0 ~ - return -1;
3 H2 \* c) X7 s5 {! w) E7 O - }
. g! o, V0 ~3 d4 g& D3 q* y - printf("listen............/ k( w0 W- ?: n+ p7 V
- ");- K+ @% S" n3 U2 m4 p5 A$ i
- / B. Y6 }( [: A/ J
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
' }- P3 t: d, f - if (connfd < 0)
M4 v- x3 M2 s; ?. R# T - {1 |) G( _% S& R2 y
- perror("accept");
8 f5 q# L7 K7 B' F8 j - return -1;& z, Z o$ W" S! a' L5 b
- }+ [0 q1 T% Y4 \# k# I5 C) j' X
- printf("accept..............
9 ]: V' t+ n0 s# t: K; @8 p - ");
+ F1 B# T) Q" j/ B8 n - char buf[100];//定义一个数组用来存储接收到的数据
! ? {) i3 |& T, r; x/ k' T* T - int ret;: @! y- [; R7 {( ]$ T( p
- while (1)# f( L0 m" p2 l4 {
- {
1 c+ a* {% S3 |) ? - memset(buf, 0, sizeof(buf));
6 D) Y( I4 H: ?6 O; t5 d4 {5 A) H+ c - ret = read(connfd, buf, sizeof(buf));
- g! v# i/ s, n3 C# i - if (0 > ret)6 |% h/ K5 R4 x D9 _0 J
- {
( ~- Z/ j; y1 }+ Z - perror("read");
" ?5 B/ l) N i+ @+ o3 q - break;
2 ^) ^% l! F% u# o) W1 A - }//执行while循环读取数据,当$ J' Z1 r9 u. |/ z
- else if (0 == ret)- s/ B* Z, k, G- m+ M
- {& \6 L+ C' f8 d' W1 @! C5 k T& j
- printf("write close!3 i3 T0 n# c. m# d
- ");- ` d4 ?3 l- M5 n; |
- break;6 L; v# v4 D8 x( e9 r
- } M; A+ U3 v7 n, w
- printf("recv: ");
8 g+ Z6 H6 ]5 @8 ]# {( O - fputs(buf, stdout);//打印接收到的数据
8 |1 V; K2 v2 h - }) Y8 f; m7 B8 N4 f: B2 k2 K
- close(sockfd);//关闭套接字
[$ W0 F( E) n - close(connfd);//断开连接
8 b* d. ~; O7 x - return 0;
0 S2 }- R1 s/ i+ M4 r0 Y - }
复制代码
, r, s3 C* m1 f8 O" v* y5 U
% }$ \- V; y) |- /*客户端*/(具体功能和服务器一样,所以不再加注释)! b" O$ w2 Y9 |5 L& A9 \( |6 ^2 f. c
- #include <stdio.h>! c4 C; }* N9 V0 z8 I
- #include <string.h>
2 t0 I6 {7 e5 n - #include <stdlib.h>
/ R5 M9 A" v$ y0 N - #include <strings.h>
6 |- c4 z8 z4 U4 c! R2 W; ? - #include <sys/types.h>/ C c6 Q2 p: J$ Q/ z% Z/ q% S
- #include <sys/socket.h>/ }* C8 }) g$ F5 @! F; d3 I6 Y
- #include <netinet/in.h>
. U( f! P. t- D. ?& S/ ]6 h/ _ - #include <arpa/inet.h>6 c [: D. {6 e9 B* `- R
- int main()
/ `4 H9 J/ ]* ?4 q - {# i* I! P+ ~3 B' j0 I
- int sockfd;; K! e; E" u& y; u3 z+ ]
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))! J, @- h S" R5 N0 M8 k1 G' G3 O
- {( |' e% M3 E# r, H4 |
- perror("socket");
* N; i4 O2 V* |3 F$ W/ S" X - return -1;
9 K# Z T' F1 `. Q9 H - }) e7 R( B' b6 ^' [& z7 g
- printf("socket...........: c& S% ~6 e, o3 y% t+ F6 j1 m
- ");# A1 T+ q, `1 I! {2 E
- # v2 T J6 I# d; ?
- struct sockaddr_in srv_addr;, e/ P4 i; y0 l0 A8 V
- memset(&srv_addr, 0, sizeof(srv_addr));! {' Z! ^5 v: h7 d
- srv_addr.sin_family = AF_INET;
8 {2 U' ~5 ^0 R+ P$ D% B4 \6 m - srv_addr.sin_port = htons(8888);
4 R) f+ T$ G$ o9 \9 f( x - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
. L& X8 E7 F( z( v/ o - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
7 |- q6 ]& A, u& e0 F - {9 C8 ~& H* O' F
- perror("connect");) T8 E, A' _ ]3 O
- return -1; //exit //pthread_exit
$ }- L; Z- Z( i7 N3 [ - }
$ l9 ]. T& S+ r9 j* G) a. F - printf("connect..............0 a/ L& j( K) g& R, o
- ");/ M8 R( O, n) S. e
- char buf[100];
/ d c0 c6 p4 [5 z - int ret;8 s' P( d3 z5 R& t* a+ T
- while (1)
, w8 ?1 o5 _. W- @- `5 y+ h - {; F+ Z7 K* b9 m( j4 G
- printf("send: ");( P; e5 j2 V9 D3 K$ }5 f
- fgets(buf, sizeof(buf), stdin);' h; j: d0 y: m: c2 E
- ret = write(sockfd, buf, sizeof(buf));( }5 C0 g, j" `3 S
- if (ret < 0) \, e" H8 B5 I3 ^, ~
- {
4 ~' H% |% o- z1 v7 g - perror("write"); a( d$ q( q d' {
- break;4 j7 g# m b4 _9 o9 E
- }9 e8 u8 e7 D9 w8 k( Y$ }
- if (strncmp(buf, "quit", 4) == 0)) i- j( o" s8 u) X
- break;) a, o9 L" ?7 ^* [8 R* g! x
- }
1 S' \& Y/ B& N/ ^2 S* L$ n - close(sockfd);
0 _' o6 w. W5 Y/ I - return 0;
+ e; L! p6 y: ]+ s T0 w - }
复制代码 ( n. Q; ?# N: s5 C4 i; z3 z
, k u4 S. s3 I: ?" Q1 \ |
|