管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。! v2 N. K. \0 I! B
# O3 W4 R# B ]3 ?- P# m* `
$ r; ]* n2 M L" t$ V4 V
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。& e, U) D* a m2 j6 J! r: f
+ u) P0 _/ \ }( N4 O ?/ X, u1 u! T. ^, X, O
TCP协议
# G+ x. |; a/ k7 OTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; ~$ t* h% b+ d. p
* k& k$ p- @3 w/ X% r2 q. a- l/ k6 ?# @5 l
关键词:三次握手,可靠,基于字节流。9 d- i' c2 o9 s
( s& |! H+ L( ?% \- U: L2 a' N( C& T( K3 ?
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。% d3 U4 E* f; o8 ~
+ t/ Y# F, k( K- h3 v W! a
TCP服务器端和客户端的运行流程
" N0 ^* q K9 `6 L( S( I5 u- w6 g" _( W( I2 c) |" T
8 _: G9 h0 x, I2 \
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
) g& L9 P- f$ M% k5 [- L
2 y) B/ ~# n; j. h# |8 s$ t. j0 b' F0 Y
1.创建socket8 T l' T7 A1 o+ H
socket是一个结构体,被创建在内核中
# q+ z8 |; U5 M8 R1 N2 N+ V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议2 [! S; E J( m' u2 h
3 A8 }2 {0 [& z1 l
% L; X' i I& K% U1 r) z c
2.调用bind函数
/ Q+ e0 _2 r6 w) F2 S& h# e. E0 ^ 将socket和地址(包括ip、port)绑定。, P& q P/ a2 C8 C$ [9 \
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序& A: s" R$ m1 d1 b
struct sockaddr_in myaddr; //地址结构体
h/ }+ H* @5 O7 j# F bind函数
5 b2 \6 ?+ c* C8 ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* W6 Q4 t: A* V- C6 x
- z m2 k! `+ G8 R
3 S. d+ | a8 e) e1 k9 A1 I- y3.listen监听,将接收到的客户端连接放入队列: G, M' U/ A) f! H3 x) p) \! d* a% R
listen(sockfd,8) //第二个参数是队列长度
4 v# ]2 v7 V% N( Z0 A! h9 j1 o- E
% _' l1 N) T. R' Y8 Y' N, N$ O* D
4.调用accept函数,从队列获取请求,返回socket描 述符; H3 v2 s: O$ W/ ]6 b- k
如果无请求,将会阻塞,直到获得连接
) I; V- X4 d4 w$ j. M8 ^ int fd=accept(sockfd, NULL,NULL);//这边采用默认参数$ O+ g- Z7 r- y; l- Y! {2 ]
! z: O& [6 _" s. l# U+ [/ ?6 A& z6 r& [2 s# B
5.调用read/write进行双向通信% Q# U2 C) K( R) m N
}3 R& L0 X9 j. F+ Y! j& C* c0 r0 O/ x* o; v6 ?, b
6.关闭accept返回的socket8 `7 F1 t" \& k
close(scokfd);
) N) ^6 b9 T$ ^9 l0 m1 Y# P* r0 P& b
# p( v+ t2 ]' O& a
5 H0 ^- P5 d2 @4 Z* q* t. h; B P, B
下面放出完整代码. `/ E7 V7 A3 X* V
6 C$ A5 U4 F. a2 q5 @* b' D1 K6 b
- /*服务器*/% d0 u/ m' ]. B' Q
- #include <stdio.h>' v$ I- X6 e) C7 z- c" D; h0 e
- #include <string.h>6 _; }2 z5 c T/ E
- #include <stdlib.h>3 v1 K7 T4 Y& _2 ]
- #include <strings.h>2 h6 t* {( N7 X; F# O: j% O
- #include <sys/types.h>" s: l1 z* ]5 h& u0 E" g
- #include <sys/socket.h>/ G ?4 I+ k, e
- #include <arpa/inet.h>
8 e& ?( e. v0 _0 E0 ?+ g) q* z - #include <netinet/in.h>
3 H7 x) Y/ c" x- y* { - int main()# {0 R" p9 n5 Q0 a% r
- {
$ n4 B# h7 x5 j9 ~! C, ` - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" ?9 H- k% i# `: u& M2 f% r. n6 z
- if (sockfd < 0)
1 }" k9 j1 m5 u' y) y1 K - {3 S/ V8 G6 U G! O$ j' h3 s6 {$ Z, D
- perror("socket");4 X, s. Y( T# V
- return -1;
( T8 D( {3 v% w! ]3 c( U - } //创建失败的错误处理
4 ]! |& i# n' Q: E4 F# y - printf("socket..............( S1 X# `% C8 w$ M2 X3 k4 o* S
- "); //成功则打印“socket。。。。”
7 h6 f* M( x# e3 R7 X0 X7 ~ -
& S1 h9 \$ {: b3 a M$ ~2 k7 W - struct sockaddr_in myaddr; //创建“我的地址”结构体
0 }8 Q7 s% }5 {$ y - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 r. c8 J- q# p( b; t
- myaddr.sin_family = AF_INET; //选择IPV4地址类型/ z5 |" K) z4 i4 n/ [
- myaddr.sin_port = htons(8888); //选择端口号
. ]2 r0 ^* F( E0 u - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址9 ~: Z4 y7 q1 z3 j0 b2 g
- 1 _ o) \& u9 R8 }- v
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
% Z7 a1 C" P: B+ Q7 E% W - {
~- m/ F8 w8 i - perror("bind");
! j2 @$ |2 j8 B# k+ _+ g. s6 r - return -1;2 t3 ] G* v/ R) G! n
- }
& o, j- ~/ r$ [/ s9 e9 y- y - printf("bind..........
* F! R& s7 o {" h! X - ");
5 h4 U: T5 f9 D' r, A" G -
% v E& v \( x1 G* B& t( M - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
3 R; @! u7 s! Z% X - {! E$ |# \, J9 i: g0 W, o a
- perror("listen");# v! `& W( \7 I0 y* h$ z
- return -1;, Z/ ~0 q/ d. I; M4 }
- }
8 n1 y* [. y4 C- G$ F7 W% s& N/ i/ a( W& E - printf("listen............
U% i: o3 L' x/ I7 c. f7 } - ");
# R) o5 R/ c- F j1 u: s) I - 6 H' U6 U% T2 _ ~
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ k1 o& v9 |. ~) V9 d9 O1 F
- if (connfd < 0)$ \2 q0 b; J. {# b
- {) i) j3 ]. e8 X7 G
- perror("accept");
5 P! U. @) N9 l3 t! g- D; v- T; s - return -1;
d7 _; \0 R$ w4 U - }
0 t" b5 @- q5 R% B* x k# w u - printf("accept..............
6 u$ ]9 {/ N9 G" m1 s - ");# A7 q2 m9 p) D& @) K
- char buf[100];//定义一个数组用来存储接收到的数据
7 r! i T1 ~6 S @, m2 `2 r - int ret;
- E4 @3 {' c y, r; S+ d# e - while (1)9 O* L* ]( Y) z' b1 n0 ]$ C
- {
* h2 @5 I, E$ @ - memset(buf, 0, sizeof(buf));4 h9 V/ ^7 N: j
- ret = read(connfd, buf, sizeof(buf));
( Z+ k3 j0 f3 A' K9 ]6 O" O- r - if (0 > ret)
* l, e: w, I7 M4 g - {
* u; f% e V! @ - perror("read");: l7 D& Z- k* A$ r
- break;
) I) ^! j" B) i% f - }//执行while循环读取数据,当, D) r# i& `- ]% m7 n
- else if (0 == ret)/ o, ^3 \6 R- ?% e: W. Z
- { h6 H, n0 Q5 r" }7 x4 }8 p
- printf("write close!8 E" K3 X4 [/ g2 H2 F* X4 b0 @
- ");
! z" R' f; m+ o9 H: ^ - break;
, b3 Z( {; m% v x3 l- T9 I - }
8 W0 f x# H0 G; I. y6 C1 q - printf("recv: ");& L# @9 w2 h: S1 G1 w1 O
- fputs(buf, stdout);//打印接收到的数据
# D( E& B9 T! e- X J - } ]7 n3 k, o) R E: K
- close(sockfd);//关闭套接字& @! |* Z) f. t* y% p+ ~! V' z+ E6 `
- close(connfd);//断开连接
" |2 G3 `$ i: {, N - return 0;6 h4 M% K ]/ {) a
- }
复制代码 ; }& I* e9 h3 \5 U3 x( j
3 I' Z0 m9 h" M" k
- /*客户端*/(具体功能和服务器一样,所以不再加注释) f- J) j7 K8 X( r
- #include <stdio.h>, q$ A/ R( _6 @$ ?
- #include <string.h>
# J/ i+ R! ^! B, {& F8 j7 ? - #include <stdlib.h>' v* u3 R/ Y' x" K4 i4 b
- #include <strings.h>
" Z* l) O W5 R/ f& m - #include <sys/types.h>6 n0 G5 }* G0 \# W0 r+ `
- #include <sys/socket.h>0 b+ i- S4 o6 m6 ~# r# V7 u: t" H
- #include <netinet/in.h>
: ~; f& k5 P; S( \ - #include <arpa/inet.h>
" B( b5 I& k5 N6 L3 i - int main()
. S+ a# ], y: S4 E - {9 }( \* m) q# x1 l+ G2 Y8 b ]3 N- k' ?
- int sockfd;
8 h T- T) G' D+ \, L - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))8 a. B6 f4 e$ t. G4 \! c
- {( \& p* o% Z7 P' \
- perror("socket");
# L' N V2 ~3 Z% G - return -1;. V2 k! ~. |* d+ Z% \
- }
2 D: L; {- D: p5 l1 l4 ?, T0 r - printf("socket...........0 K7 q$ i& p7 d) k4 ^
- ");
$ n8 q3 T1 f( F8 o2 ~+ l ^ -
! f1 a4 b/ V8 ~" ] - struct sockaddr_in srv_addr;8 L- J& A1 a# b: l0 k% C
- memset(&srv_addr, 0, sizeof(srv_addr));
0 ], z8 ?6 m% _ }6 ?' t! u U6 L - srv_addr.sin_family = AF_INET;
) f5 j2 A" l, z1 q - srv_addr.sin_port = htons(8888);( I$ B: O6 M) x+ P5 b4 Z! O
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");: O$ X8 I/ } Y9 O; Y' h5 J; M
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
# L' E# l3 \2 \( d; S9 i - {
0 C! X. g( A6 O& }4 a6 t - perror("connect");
3 S) P# P: R Y' m - return -1; //exit //pthread_exit4 g0 M& ?' M5 }9 t( m, _# d; g
- }
_0 ?, n6 r! Y Z7 I2 `, }. G - printf("connect..............
3 r2 \ }! S$ c. J: P - ");
% R' ^- X7 A) t, I' y' m, L - char buf[100];
2 E, J6 _$ h" h0 \& ^) _& m - int ret;
s. O2 m n* v( {7 g - while (1)
8 C' h, w7 C) a% P, u8 G8 G - {
! |- z' Z+ T1 f2 }6 }) f' V& i - printf("send: ");4 Z0 p9 F5 z s) l9 w# \
- fgets(buf, sizeof(buf), stdin);! M# Y. M% j" V3 @6 j. ~2 f) ~
- ret = write(sockfd, buf, sizeof(buf));% c% x" s6 P \3 u" a
- if (ret < 0)
8 `( R. d1 `, g$ G - {
9 _8 S- n! d4 \" `, [" v i* M% S: E8 ^% I$ { - perror("write");3 q7 _/ Y: a: @9 G8 U% b
- break;" W" P) C, Y' o$ P
- }
. b8 M% U& c. u! J9 N0 |: D* a9 i - if (strncmp(buf, "quit", 4) == 0)) \* Q# E5 V$ }8 p8 X
- break;5 a9 W0 w, I8 S C1 l& P% E
- }
# f7 A f8 A, {! w7 n$ M9 N4 ?+ z - close(sockfd);
% m( n& G" \' m* P' Z1 T6 n - return 0;1 n/ O1 q {- @% d2 W
- }
复制代码 ( T) G. k2 H% e( y A6 v, `6 e
) W# d" ?8 ?6 m: d( M |
|