管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
) b( i% N+ U2 @( ~! ]% f7 ]' e9 ]$ G3 U
7 S1 ~5 y3 e- w. gsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
8 M# q O8 B) G8 j; `( h i, }
3 }' B$ g. c* n& }* B. Y- U' a
: k, O$ `# \) \7 DTCP协议
1 T, b: a3 b$ n" n/ S& cTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
$ i! Y5 I7 G4 a9 N1 h, T- v* y6 Q, \2 t. z
" n" _% u/ T% Y9 e8 d. @3 a关键词:三次握手,可靠,基于字节流。
- X: x9 }0 y1 o/ M: e# F9 E: D" Z' S
. W& d& E$ w8 F; X$ m! h" Z; f/ B4 p0 B7 S4 P- k
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。8 ^3 Y, @; p! ?; o) _ |3 `
8 q" u* u+ e& ]& q/ [5 R, m0 y
TCP服务器端和客户端的运行流程
+ |. `; i b$ e5 N
5 o% p$ R1 `5 {( K$ Q2 ]& ?: ]* n, [
" R/ o! \+ W* `: M2 p如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?: c4 {5 q( y# I1 A3 H( K
+ q K* {' V% Q4 L' R
- J; a7 P1 P$ D+ L1.创建socket& N, W/ D8 Z8 e: e
socket是一个结构体,被创建在内核中
. Y, l: c/ S3 L+ z8 V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议( v. p5 n9 V2 k6 w1 g; ^" Q
: N8 v# n$ T0 ~& F$ Y- o3 I( N# Q( W! A
/ X2 T8 }% `1 L8 ?5 G2 O2.调用bind函数8 t: E; Q& A- _, A8 L. u; D O
将socket和地址(包括ip、port)绑定。0 K# I2 p. n9 u$ _' V
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序- h& Z& j, O! G+ ~$ x
struct sockaddr_in myaddr; //地址结构体
$ N/ m" J+ @9 ^8 y1 E$ l/ M bind函数
; R/ x* u1 _9 W, Z bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))$ b- o8 e( ^+ e) R8 Z
+ _5 P% |0 q1 }3 |7 ?) Q$ |& v
# A* o) ^, R7 `! t8 g; @3 Q( e3.listen监听,将接收到的客户端连接放入队列
) g# `" B2 F# U7 v( r" @1 L8 B& d+ n listen(sockfd,8) //第二个参数是队列长度8 y0 R8 V: D0 I1 J/ x
+ d7 ]2 Y! D6 W0 ]3 x0 x3 u' S! Q0 ]; w1 a
4.调用accept函数,从队列获取请求,返回socket描 述符
% d" | T* o( {, R 如果无请求,将会阻塞,直到获得连接2 E( I; n9 r# H8 S2 h
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数! J5 P) Y4 e7 b& d6 q2 P7 t4 a
/ M& [! h( Q: m# L
# G* m* r9 m/ N6 A6 z3 j5.调用read/write进行双向通信
: _% k" i q$ L, ~8 O+ r
" d8 V a l; A2 e! ~% B, B( u) h
+ o& t/ e" Z+ x" [6.关闭accept返回的socket. U/ p3 ]% Z, F l* w" K
close(scokfd);- P, R* N* w: V8 j9 ?1 W) h8 I
& U6 X# g, G2 ], g
+ Y! S U8 r, p( r0 ~5 ^4 m8 S
, h2 Y1 e% D- K' y g: ~$ ^& L1 n+ ?* D- y5 K6 Z5 h0 `) w7 m
下面放出完整代码
3 J9 j; K; U4 \6 V5 x, f4 _8 w8 ~7 k o
- /*服务器*/
: P% a% r9 F+ L1 Y& ~: F - #include <stdio.h>+ V5 q9 }1 U0 `, c* I" B2 A b2 U
- #include <string.h>7 k) B1 v1 f# _- [" c, @8 F
- #include <stdlib.h>) R7 d1 }/ x4 W3 l* ^, i
- #include <strings.h>7 ~% K5 D, K0 I
- #include <sys/types.h>
; `; S& c# d" i4 y$ I9 A1 k- z - #include <sys/socket.h>
8 O/ n H+ r% G: i' L9 d - #include <arpa/inet.h>2 Z3 b- Z5 V# E. w
- #include <netinet/in.h>
' P+ [- E1 m- s/ \, J - int main()5 r1 L- b7 p8 r% s5 o. d
- {
+ h* Z$ }- I7 o: y7 R - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字5 m A8 ]+ g! S2 C, I
- if (sockfd < 0)
" E2 H2 i+ O7 C: M6 }% x- t - {2 T8 l' ~) f, y6 Q, S, A
- perror("socket");% t7 S; N. a4 M% c' K
- return -1;
; U; j: {/ Q3 Z7 i6 y/ m - } //创建失败的错误处理$ y7 Q- k$ l' }/ a w
- printf("socket..............
" U8 u. U7 Y% T* O/ d - "); //成功则打印“socket。。。。”' A7 q. B, U* s: z. [9 v# `5 o% n
-
* v9 U$ ~) N/ \7 q - struct sockaddr_in myaddr; //创建“我的地址”结构体5 l' \. A7 N5 d6 }( k# ]: S
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
/ g7 k! g6 V" V" P( g9 M% K2 Y - myaddr.sin_family = AF_INET; //选择IPV4地址类型. t1 ~# b: g3 e6 D* u3 \
- myaddr.sin_port = htons(8888); //选择端口号% V1 I } D! t* n; u' f% @
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址" g5 l& \' {2 w' h" s
-
& a: I4 c8 b8 k. Y - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
1 J" O- s' V( J7 g& U P. N. a - {; J# B& F; s- k7 w, w, \
- perror("bind");8 x6 B! ]' A) K$ R
- return -1;
" l+ [: Z; }9 e5 V. q1 [$ { - }
0 h/ y) w$ j; T - printf("bind..........6 f0 a* }. Z, C' a2 i& W; V; D
- ");% t2 c6 X( \8 E% N7 `8 f3 {
- ) m% W1 H( U/ A) _ t5 u* g0 a: G: J
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
/ ]9 q' S6 b# z/ S - {
3 j; {0 }$ x4 J* R( N# [7 X - perror("listen");0 W3 \% ^& S! Z% V
- return -1;
* O$ J4 W( {; w9 K - }8 k: ^1 K' ~! r: z% o7 U8 @7 I
- printf("listen............
( E; i' A' x5 o; N: p - ");
! j' n3 `7 E6 i - 3 l$ F9 f' `9 k% D/ F" n
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求7 I; |) @" `& D' e; c4 K- Z+ x& y# J
- if (connfd < 0)6 ^/ ]! x6 G: M2 Q* s
- {- d. T' S. T* [$ T, i
- perror("accept");. ~( w9 | {7 `" \( x8 ?
- return -1;2 }: _8 C$ Y! z3 b7 s* H
- }/ |0 i5 y$ V% r# |' ~0 o
- printf("accept..............
/ G4 W! q) [1 [7 V; u- c2 }& w - ");
' U2 R; f7 G' a' C4 G/ E- X) y - char buf[100];//定义一个数组用来存储接收到的数据! v- q; O# n$ K9 n) d) c
- int ret;
; L3 g* N8 Y2 _ - while (1)3 ^4 I- ?+ i2 Y) W2 M% U
- {
5 ?+ R& q5 e& r" C) `2 q - memset(buf, 0, sizeof(buf));
8 I V) t; l4 a+ e6 m1 N - ret = read(connfd, buf, sizeof(buf));$ M3 V6 Q* H' ]. T/ q5 r
- if (0 > ret)
' Q' b( w, m2 f. k$ S3 p( h/ n - {0 M0 c+ D9 l3 B: T- y4 J
- perror("read");9 ~2 Q% d! R* G8 h% _
- break;9 f7 [) v6 m7 b' {6 f: A" A8 t
- }//执行while循环读取数据,当
1 M/ O4 e+ }8 O - else if (0 == ret)
- @( R6 i( u1 f1 L7 U - {: ]7 o' z' t0 m+ l+ r1 j
- printf("write close!/ H' V' p; x8 f& w7 M
- ");
4 M) ]! z* D! ~& s: M: c' Z - break;
' Y' u \/ h( ~3 ?+ m. d - }0 w5 M b) p9 o# h& _2 `$ W2 J+ n
- printf("recv: ");
! u# q8 ]# D+ r3 H# G# N - fputs(buf, stdout);//打印接收到的数据; o" \% {8 L3 d+ R0 d7 q
- }
& [3 y3 t4 t a/ q7 v; ` - close(sockfd);//关闭套接字4 g3 z" C1 k m2 Z# e
- close(connfd);//断开连接
2 i' a/ O" N6 B9 `1 t/ |9 h4 G - return 0;
. B( W% X2 ?4 Q9 P/ A$ b - }
复制代码 + c% |/ w/ K, N" Z. R+ m- x
$ }( U) Q9 B/ l- /*客户端*/(具体功能和服务器一样,所以不再加注释)) U1 p3 z. E* G8 i
- #include <stdio.h>0 Y/ i) p. e! U4 I; [; U
- #include <string.h>
& m N3 M8 {. @/ M - #include <stdlib.h>* J& B) ?+ A2 H" |
- #include <strings.h>
+ z' q, W) p3 V - #include <sys/types.h>1 u4 B4 b( D, V; d6 x
- #include <sys/socket.h>
c# w/ m$ Z9 f8 P, s - #include <netinet/in.h>; G4 ?2 P6 t1 q( K
- #include <arpa/inet.h>
0 B6 j5 q9 ]# x$ U% T3 s - int main()
4 q" R4 Y* t6 U& [ q2 p& l - {, C( B" c* Y$ c* H
- int sockfd;
/ n; t2 U! U! [) `3 s1 Q% a - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
" j/ Y% X& K0 W; M$ s0 Q( k - {) ]' K2 w7 M4 q- n
- perror("socket");
- _0 l7 D9 o+ a8 d( e0 s - return -1;1 f) s* n0 P- D4 G0 B7 O0 ]+ |- o
- }
; u3 Y, t8 _0 e0 { - printf("socket...........( o$ b4 y* Q. I, c" m
- ");
& n4 T! H N: u1 j" D/ | -
2 t6 }6 A* w4 Q+ z* { - struct sockaddr_in srv_addr;
p2 a9 B& y x9 [4 T3 U - memset(&srv_addr, 0, sizeof(srv_addr));. v0 H: Y2 x. Y8 x0 t8 X4 q- ^
- srv_addr.sin_family = AF_INET;: N; P; H. V) i* |) ^! V- C7 ~0 T
- srv_addr.sin_port = htons(8888);
( d# F0 s; p& m0 b) a - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");. L7 @* @! J1 B {; V' |" T
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
, Z1 i% Q4 `1 V - {
% X) K, s4 O8 |; i) \" l" b$ L) ^ - perror("connect");8 T0 f, b' C" t+ `$ V7 @
- return -1; //exit //pthread_exit, z5 _: g! X) Q
- }( e. j7 P1 J( Q8 @1 J! Q! V7 z
- printf("connect..............
2 v6 D" N& J, u, c0 G2 @) G2 L1 L - ");
0 h# \- B% w# L3 J0 K) I" S- z - char buf[100];# X" E6 z( ?& Q: [6 M y3 i6 m
- int ret;; ~3 }' B: w |0 r+ x! D
- while (1)
8 Z% G. v- ?) ~6 ^ - {! _0 M& j7 @! }5 V7 \1 n/ N2 Y
- printf("send: ");, B% q( j: `& ^8 v# e
- fgets(buf, sizeof(buf), stdin);8 X6 l9 M% G" _7 [( X
- ret = write(sockfd, buf, sizeof(buf));4 ~% v6 i) L' q- N
- if (ret < 0)
' r8 t. P; U, @, Q1 c' u - {
! o" p' d5 n4 [/ r2 ~- N7 @ - perror("write");
5 [( t: x, o; Q( ^+ X+ v L - break;8 [% C& z6 [$ d) c7 V
- } [* t9 X6 R: [; |8 ]
- if (strncmp(buf, "quit", 4) == 0)
" [9 B8 q% m7 ^6 V, v - break;/ X+ R3 F6 R' }! K9 L
- }, N0 H2 D6 N1 M, u1 q
- close(sockfd);! j: n& B b3 y' g7 `+ `$ i, S+ L
- return 0;3 L% S# d+ q _
- }
复制代码
8 e4 i5 E. A. @3 j! k9 C' @4 _) C* @
|
|