管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
5 o( K4 x/ R) I% x% P" }# [& W1 f
" x, g2 d$ x* v0 F# f/ u
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
2 Z: U5 N0 O/ b. |& ^& f D8 ~' U& z. k/ X. g a
8 h( ?3 y- ?0 sTCP协议8 q, r7 I+ O/ L; W
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。% S& F$ M' i. _
8 D- j& ~/ F1 a1 [: o9 @* k9 w1 i2 N' h8 i) F
关键词:三次握手,可靠,基于字节流。2 K1 q9 @! ?: O7 B
/ a' C( r( I5 w+ o/ t* M* D; H
) R, Y6 M$ r1 a- I. v可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
% V8 @5 M6 v4 B. r% E
* R6 \( C$ v/ h- u' ZTCP服务器端和客户端的运行流程9 j+ h% b% Z1 Z) N9 @: k8 ?( Z( Y
$ }& l* b+ L* X' o% `& s
" Y8 b7 }- W+ w* G$ x/ d) N T如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?; y7 e5 m, [# D6 e. k
0 } ] B4 }' ?2 {% Z& I
, M: M2 [3 p( A1 Y1.创建socket- a/ p- N) S7 J+ Y @
socket是一个结构体,被创建在内核中
+ M& R, P# Z1 m3 i' E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2 g$ ?4 h* c7 Q4 t: I% W) \/ M1 g1 E0 Y% H$ f2 y
2 H5 q) j+ [$ B' \7 t
2.调用bind函数
( B0 G: I+ x/ i( }2 M 将socket和地址(包括ip、port)绑定。
; A5 x* w% F" V. |" O* s' {6 o) A' { 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序2 j+ h+ [% n1 r$ B' a
struct sockaddr_in myaddr; //地址结构体0 L, M5 G1 W# c4 x- ]+ W& V3 S
bind函数 [) S7 y( F; ]% S
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* Q7 P( }0 _; ~3 i1 t" b6 b2 B. @3 T/ \
7 V/ {, u' V1 |+ \2 ]6 |5 f6 C# [
3.listen监听,将接收到的客户端连接放入队列, w9 Y6 w8 Q# Z
listen(sockfd,8) //第二个参数是队列长度
) [6 f6 A U" O) l- B5 y4 I1 C% \# p3 ]; b. [" X# [9 e
0 [) D/ D* N- ` h R6 t5 e4.调用accept函数,从队列获取请求,返回socket描 述符$ l' C: _: c9 W* q- Z
如果无请求,将会阻塞,直到获得连接
* W2 D" v5 @7 [* L5 O2 o2 U int fd=accept(sockfd, NULL,NULL);//这边采用默认参数+ ], M* u4 ~8 e0 e8 {/ J C! V# N3 y4 H
, \& t2 @& n1 C0 a
4 `0 f: A( y/ s2 c: r% X5.调用read/write进行双向通信
% h& `/ y- b3 d) M: c: x& v, @6 Q3 }) J2 n) P& j
" b7 I1 g) ^4 `- O7 u$ ?: e, \6.关闭accept返回的socket9 I& P9 c( L0 D' `- Q
close(scokfd);/ ^: O& P0 s4 K2 b! Z
4 W+ h+ g- |7 C ]4 {$ A# d9 A4 F: }4 x1 z
0 v& H5 z3 k+ N! s$ V T Y" ^' u; `/ W
下面放出完整代码
( ~; R( M8 c7 b" S: V, J* O; O# Y( ?" B' g6 ]* b- O
- /*服务器*/
1 B d/ X$ Q- O- ? - #include <stdio.h>1 k, ?( u t- D" Y
- #include <string.h>: @: {6 n9 S. W. K9 z
- #include <stdlib.h>/ j$ d" v8 U: h2 V; H
- #include <strings.h>
' F& e5 S; }2 U% K! m8 _1 G# i - #include <sys/types.h> r4 d& Y- K8 @3 E' \! X7 M
- #include <sys/socket.h>9 W- u7 p( w; X# [5 A& J& ?, L4 i/ p
- #include <arpa/inet.h>/ t& B3 H, E, v- c7 t+ W
- #include <netinet/in.h>
+ O$ k7 s# {, ?* j6 r3 ~) G - int main()
6 W( f* A5 D; A - {! r1 {4 O1 K3 ?) r; n6 |
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# }+ m5 }" D# U. N7 Q# C1 X% q
- if (sockfd < 0)
5 C0 a, r6 ?0 D* J2 I - {( n- L/ I) D" I0 e" ^/ H
- perror("socket");
% \; O6 X% h! J' o2 z' {4 t - return -1;1 h8 n+ A$ o9 f
- } //创建失败的错误处理' x" d2 v% C) u- \6 ]" m7 y
- printf("socket..............
1 ^5 g! V9 f0 O+ d% f - "); //成功则打印“socket。。。。”
$ k. c" X- f) F# Q3 }( P/ z -
4 D( @. o( O i! g1 O9 u$ ` - struct sockaddr_in myaddr; //创建“我的地址”结构体
0 r: j/ Q! y8 ~5 L$ y" y - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
! {# R' A D5 l' V; \4 l - myaddr.sin_family = AF_INET; //选择IPV4地址类型! X7 \- _4 r S
- myaddr.sin_port = htons(8888); //选择端口号
: b) q8 l& x6 Y- N' [" C# T0 M - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址4 S* ]- T/ P; R ^& P6 q
- . X- J; F7 u) m8 s$ l
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
2 J# M+ \4 ?, y$ S" C - {
/ k/ h# M7 _5 V6 s - perror("bind");5 D0 k0 b( w4 [( P/ |/ R
- return -1;0 Y) p1 B; }1 Q5 O/ B
- }( m9 y! q3 _+ W
- printf("bind..........2 M) f2 o% }5 M) z0 V* c7 k" O7 x
- ");2 j" t7 d! T, n1 f0 q0 _
- 3 o8 W: f) J0 }
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
/ X; u4 J, d a2 O! Z$ J( X( k/ B4 D - {6 X/ Z4 R. i9 U! r6 v1 F0 U1 I
- perror("listen");
1 y" d, b" L! `" W- ~ - return -1; z2 l2 P' o) S/ h8 B
- }
0 E: Z! Z) z1 X - printf("listen............
' c% b* W+ D0 I - ");
0 f- [" H- [: v - 3 h2 U( u' m1 n
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求' v# e& o/ V/ S* @* [) s
- if (connfd < 0), N, B3 L$ B/ ], P, ?
- {
% }3 r' o$ M# w$ y5 V5 W. A7 L - perror("accept");; |4 M* }* ^( [* W
- return -1;
% W5 z! _, [* X" W6 @ - }
7 u i. M6 {( v0 L3 ?; s - printf("accept..............
6 m( {) b4 O: Q# k2 r+ Q - ");
7 M R- ^& f7 r5 G* @# Z - char buf[100];//定义一个数组用来存储接收到的数据$ x C; s9 h4 e
- int ret;
; |7 `% I6 r2 O. y" \; Y2 x' C! l - while (1): Y; q: a$ ~" d, }0 H: t
- {! W1 Q3 Q3 |, f9 ?2 d: ~2 D
- memset(buf, 0, sizeof(buf));$ Y0 i. m4 T# U! o8 J& V; b9 ?
- ret = read(connfd, buf, sizeof(buf));/ Z( M V) i- [. }7 M0 a
- if (0 > ret)
1 Y5 E# i N$ h - {
' X. O$ c1 g( p6 |1 V3 V" _ - perror("read");
0 Z/ m, w; R e. X7 } - break;+ r# W2 y; }" K$ y( y
- }//执行while循环读取数据,当# E9 ?9 R6 g( n4 B3 R/ G5 p
- else if (0 == ret)
0 @; ?% @7 `* c% c% p - {1 W- Z" C9 u, z( o. \7 A4 m
- printf("write close!
( P$ M1 j/ u8 {' e& E( ]- ^( w - ");
. C& _4 t6 F. Z2 l8 X - break;
) g- }+ r: g' B1 h; e9 l - }
8 |/ y- ~2 O9 o4 L/ d; U - printf("recv: ");6 l# n# F/ U" ]( h; o
- fputs(buf, stdout);//打印接收到的数据
3 w' r% z$ k2 W% d0 @% H, P$ c* |( g - }" H: j0 X; M' M( P, A
- close(sockfd);//关闭套接字5 C; S7 o' s/ ^/ g* ]: B0 J4 q' m
- close(connfd);//断开连接
& _/ W9 U7 b2 \' H2 N- v - return 0;
+ y: f* U% z: p K - }
复制代码 ( Z4 Y% Q1 s$ M% N: m( B, n
+ s |; y; t5 C1 s- /*客户端*/(具体功能和服务器一样,所以不再加注释)
9 E8 @& G4 `2 l4 A - #include <stdio.h>
2 K$ E5 E1 m0 Q+ ` - #include <string.h>6 |! D& X* J" D/ u
- #include <stdlib.h>
3 e0 R* D& |( Y. ?* f: d8 n - #include <strings.h>" G N; g" C' p0 m9 Q
- #include <sys/types.h> X9 ?& o4 ?6 k; d
- #include <sys/socket.h>0 R j2 h1 ~) I2 p$ y: ~/ P
- #include <netinet/in.h>
: j5 v# h7 {: R5 X' J: ~3 A9 y - #include <arpa/inet.h>
, ~7 e% W: ]. U# x! d - int main()
! f X# j5 G9 y& j3 A - {/ u9 `6 o6 Z, V/ Y$ U( x! m
- int sockfd;
& G( c+ X8 w. ~! w/ P3 r9 |# f/ p - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
5 Y) b0 Y" o! t7 Q V% Z - {
( t* Q9 x6 U0 j9 D - perror("socket");% g: N9 D; C. _7 b/ b# X" T0 j
- return -1;/ @; q* n4 K- R7 i$ G8 U
- }5 Q3 g$ c7 W" s/ K, j# {
- printf("socket...........
+ p t" ^9 G1 K' A( F - ");
, F3 [. H; ^0 z+ L, j' V( T% J - # F# E) E, Q' A
- struct sockaddr_in srv_addr; I! ^6 A4 I# N$ T
- memset(&srv_addr, 0, sizeof(srv_addr));
2 ~5 s. t# V: k - srv_addr.sin_family = AF_INET;
. K8 @- X/ a* a( H - srv_addr.sin_port = htons(8888);: U0 N% ]; \* v; f2 `+ t8 U
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");" f% F. v7 b$ I9 C! x& c
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))3 x( @4 {. n7 H* a$ g# X
- {- x) L1 D' k4 d5 x' n
- perror("connect");. D/ S. n p* k% n. s8 x/ L
- return -1; //exit //pthread_exit
& T( R9 p; X3 a/ {9 Q - }
$ k, J% M7 v$ `1 ]/ Q - printf("connect..............9 g5 p) S( }$ p5 t$ C& a4 Y
- ");2 `( Y, t& u0 I- ?
- char buf[100];2 o; D, G6 r% c* `
- int ret;
' R) E) t# C+ Z+ I" ?9 }$ K7 s W0 t - while (1)
; Q, m% b1 _& v" ?6 A - {; Z# [* V( ^# @
- printf("send: ");+ |& i8 Q2 [* d" B+ x: w/ i
- fgets(buf, sizeof(buf), stdin);' u9 r* N' F" V/ L" v
- ret = write(sockfd, buf, sizeof(buf));
6 _ }, ^( M5 t+ R6 C$ J! {' s4 e - if (ret < 0) N% b! @" F! L; E4 o
- {
# U' j6 f2 N& J, Q - perror("write");5 V5 {0 \( W+ }0 e3 h
- break;
- Y' J6 ~; e9 l- e' r - }; _4 M) v2 B2 Q( ~4 u
- if (strncmp(buf, "quit", 4) == 0)
9 Y9 r9 J9 w* C$ j - break;: q9 N. p5 q* k1 P, v; `. z/ y
- }
. W D; W2 u, M" ~" G* l$ W g - close(sockfd);7 K e- [4 A: z
- return 0;
a, Q% O* E5 _2 ` - }
复制代码 ) j, y6 i+ L& k+ u$ z
5 N+ |8 G& G9 p, F, q S# y! V6 X) V2 o0 Z |
|