管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。& p: o$ r# t6 `
+ m9 I0 d& R/ Q. e( Y- J" F4 i
% V- U3 F, Y F0 w9 |' s- Lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 Z0 r/ u }$ g; g+ n6 Z
$ @2 P$ ]! \$ V. H$ W# T( z6 d) L& b; c" A4 R$ n
TCP协议
7 J! E) f+ ?) c) E( V5 wTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- E( ?0 d5 y7 C' r6 |6 M8 Q
7 h Q) a1 f$ E% U, i4 U+ R
; R; c6 x5 j6 h5 Z4 @关键词:三次握手,可靠,基于字节流。- |. B. K3 |. C. a! A2 z
2 ^+ w1 g" }8 Q3 H
: J' _$ Q/ f$ S' q C O可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ j* G3 V! o6 w+ ], y( \6 Y! i
9 w8 n! t3 P, G5 ^) D/ f) A; o) H
TCP服务器端和客户端的运行流程
; c, x& b) C9 d; }) Q I
" w. x e6 x4 q9 ~9 _9 J# @8 |( r8 E* W% B6 Q; S
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
; ]: }) g) y( G9 d$ t. w& ^6 Q
5 l; } K% X9 N( R+ t" {( }8 D! j
1.创建socket% {& T- L- x- E, K$ \+ x, `, ?
socket是一个结构体,被创建在内核中6 ^$ u9 l, O, l' @8 R$ W B
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议9 x9 a3 \: x8 [
' ?( a$ y5 J7 J% m' O' W' N# t5 |& ]8 G* S& E4 p" [# l4 Y' D
2.调用bind函数
/ A; ]1 t) }5 p/ L 将socket和地址(包括ip、port)绑定。
; r" @( L2 b4 M" o0 | O5 z 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序: V. i6 p. G' ?1 u# D* G3 d
struct sockaddr_in myaddr; //地址结构体
5 H! j; g I2 E. ~% q% V) e( q bind函数9 k5 t0 \4 z% b) x" o3 Q4 N* f
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* }. D2 r# ~1 I- H$ z
; M7 T# W8 u9 H2 r0 R. e
3 y- D Z: W/ }' e& l/ F3.listen监听,将接收到的客户端连接放入队列
) H. L' C' ?( g! u8 `* @- r7 I2 k listen(sockfd,8) //第二个参数是队列长度
8 s/ \" g9 p8 i+ x% u. O% d% K! _: |, v( e; z. V7 ]
, O- `7 t; M7 g+ b# D, G
4.调用accept函数,从队列获取请求,返回socket描 述符
9 h1 ~& V. A4 H1 p3 a 如果无请求,将会阻塞,直到获得连接
8 t* e& i3 h7 d9 ]% V; f int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
4 s6 Q# e# X( M" n4 H( c; H
. F/ k1 v7 I( L- N" n0 K7 X* T3 i7 C4 R+ ]9 G+ N; i1 Y
5.调用read/write进行双向通信
) S+ c" B# c; j, s* h! w* u) Z' f5 x6 S4 J6 g& h
- g2 }' e# M- h8 H1 N% t$ X: N6.关闭accept返回的socket4 ?* _& O+ ], c3 w
close(scokfd);
: y% \$ i1 c+ @8 U: V1 e- g( q- Y( M' I6 e) Q: U: y6 t
3 ~! A, ~& l9 [7 @5 O) c
' h/ |4 T2 K- q. l+ f% \
5 ^( \0 W. i8 _& g5 ]
下面放出完整代码: P7 j/ T! c" @: h' H8 F! M
! ]3 l4 G8 V, ?
- /*服务器*/
7 p: t8 o, I5 Z2 Z2 w: a3 n - #include <stdio.h>
6 i) B. F+ {$ p% e2 c - #include <string.h>' E y! h# ~* k
- #include <stdlib.h>
/ T9 N; g3 ~$ B) D1 P: _8 \ - #include <strings.h>
8 x0 k, J: B( b0 _; l. _: t% K - #include <sys/types.h>8 q. x1 K, c9 \$ L' G( O
- #include <sys/socket.h>6 t6 ?! l7 V- x, E* [# F/ u
- #include <arpa/inet.h>( Y6 i; M4 ^2 i* f1 s/ ]6 k
- #include <netinet/in.h>
0 Z9 M2 A, n8 r& \9 w- Z! P9 y+ j8 @6 ^ - int main()% v* D; {% e; \" z; d
- { ?) b! [- Y1 q' V, f. c
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
/ I% V" j0 F7 e% ^ - if (sockfd < 0)
5 d5 K; i$ T7 n3 L. b. [( Z - {
$ |0 w1 l, j0 h% W; H; y - perror("socket");
. J% m( o# j% z3 r: c1 ^. T - return -1;
6 s s" t5 b" z1 l( g% |# X6 [) L# T - } //创建失败的错误处理
$ ^; Y! r) |$ l: V ` - printf("socket..............
# ]5 b2 s1 q1 U; ]7 F1 o - "); //成功则打印“socket。。。。”
# M5 w7 d! L1 T* g t: u - 0 h0 i% w: Z2 Y1 ?
- struct sockaddr_in myaddr; //创建“我的地址”结构体
8 u& ^ `" g' K" t9 U- A8 J - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见) X6 Q7 [8 G2 T9 v
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
( u% x% d9 g/ Y( O9 y - myaddr.sin_port = htons(8888); //选择端口号
% T- C9 A2 d8 B8 d9 {0 B - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址6 Y/ e S- e0 w3 |" j
- 7 _# M0 Z5 N/ }+ a' s* o
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
3 X A3 f9 M, U# n5 ^' U: G - {6 b; g W& |2 _- `
- perror("bind");
8 @4 h8 d/ m5 J& k, h! W& ] - return -1;, ^& `- N" ?7 A. Y# {( N7 k
- }; S. M2 `3 A$ p
- printf("bind..........9 Y6 _- ^) O3 P9 U U( p% `
- ");
" s2 H; [ X$ I - ) H, a; p0 [; n7 |3 i" P
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
- Q# c& A1 r# v3 `: U - {7 d ~- i* U( U
- perror("listen");
8 J1 N6 Z4 h, l+ A - return -1;
4 b, A, M5 V C' q" ? - }
+ Z# y( d! d3 n6 f* {- g* p - printf("listen............
( j9 [5 S t9 t, _ J, q - ");7 r/ @/ T& h6 ^- }! d
-
& ]1 O! b. y- i - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
]- z/ n* b. i2 a# i' f: B - if (connfd < 0)0 g9 a& e7 W9 p( H, D) J/ z: T
- {
/ z; d b1 U9 m9 b4 C - perror("accept");6 Q2 k. ?0 _9 P/ ^) T! ~
- return -1;9 k# X- v- {4 `5 W
- }) F6 a) `! H |, R& v2 E
- printf("accept..............7 [1 H e# ?% l' z* m
- ");. F& a$ S' l! |8 c, }; R
- char buf[100];//定义一个数组用来存储接收到的数据
) x4 w$ D" S$ B - int ret;
4 o9 ^8 z2 q3 M V5 s - while (1)
. K3 I0 N6 b' i( U4 z- ?' ^ - {5 A9 _, e! U% K8 ^1 q
- memset(buf, 0, sizeof(buf));
1 S# b c* A2 x - ret = read(connfd, buf, sizeof(buf));& z w6 F& j8 L; \
- if (0 > ret)# h: X% ]8 {9 T* u
- {
& |- W1 V& r# n, H - perror("read");0 Q& h. I( l; _2 N( W7 K$ A
- break;/ T. C4 C6 X. M0 p9 e$ ~
- }//执行while循环读取数据,当
y* o' k6 V/ u$ J - else if (0 == ret)
, m% c+ R2 C; Z: ^- x* [$ v" c4 t - {! H$ ?. O# X- t/ Y' x
- printf("write close!
1 b5 @( B, Q5 I- v1 p& H - "); r9 a7 R6 \% @
- break;6 F2 w3 A4 n! p
- }) \5 |) c9 V0 z9 v
- printf("recv: ");) X& O* n! I# B. J# {* C
- fputs(buf, stdout);//打印接收到的数据3 s; l- V! ~! \ _4 G4 I
- }' j' k, ^8 ?( V; J
- close(sockfd);//关闭套接字# R; X* q: e7 [! ~' P {$ c
- close(connfd);//断开连接: {$ t/ g4 R" P4 ?" i1 x& Y s+ I; O
- return 0;
% {0 m% j1 [% c& H - }
复制代码 4 _" f( }! c. c, i
6 H; w3 I6 G: Y
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
/ B. ]8 Q7 }4 A4 ] - #include <stdio.h>
+ G4 c4 p6 a7 E - #include <string.h>
' p. }; J, j* ]; ]/ j+ H. N9 p - #include <stdlib.h>* }7 h/ x- y+ W/ |; h
- #include <strings.h>
9 T; M* I, N& T" ~, a+ \2 O, w - #include <sys/types.h>
9 F9 e) l1 u0 m" E, e1 i - #include <sys/socket.h>
& O6 B% @. I$ C; [! i - #include <netinet/in.h>
5 c% G0 l( y% l, i+ x, S: I- ` - #include <arpa/inet.h>
4 L7 g2 O7 U* x/ b6 S+ | - int main()
0 c- f9 |6 B) H" p7 A - {
1 [7 I q5 [0 n$ V' W l* z - int sockfd;6 B' s `" L1 i; C
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
t# ]: u0 i/ `# o: O - {$ m& g5 Z( k! p) y
- perror("socket");
. x/ ~! u$ R1 M- n# D7 s - return -1;
" g$ i9 j8 E7 o8 M9 g - }
5 V+ j) x! r, u1 f. i: n - printf("socket...........
9 b! i9 B) U" m) N L; o8 o - ");
+ L) m* t' p% x) g# X! t - ' F- Z2 I/ O0 `* h
- struct sockaddr_in srv_addr;
0 B. T' R/ [3 _9 F4 ? - memset(&srv_addr, 0, sizeof(srv_addr));
! L2 M# Z; l' j2 i4 L9 v A, N - srv_addr.sin_family = AF_INET;
. H& F0 M4 R+ e1 [$ m - srv_addr.sin_port = htons(8888);+ p7 d. d `% c1 r' U
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
2 u% \' n' f& p. @5 |& Q/ r0 A - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))( Q/ o8 B! B! h$ \8 W4 G# v
- {
. w9 C: V+ D- j8 z - perror("connect");
6 c# I6 U1 Q6 |7 h% v - return -1; //exit //pthread_exit
. D2 [0 Q: ?7 y0 e - }3 m' o% A6 B7 a6 L- g/ s
- printf("connect..............( G% X& \# f3 @9 ^$ v' h2 L3 W
- ");
2 y' e {; ?2 J8 N8 s' O$ T% ` - char buf[100];
9 f8 [/ E! u* _$ y: | - int ret;
* L N- C0 o. s - while (1)
* e& u0 Z+ h, E+ V. o9 X) }: p | - {# z }7 W) x1 B% k: }2 H
- printf("send: ");. k) W' w5 D9 p" p" _& e
- fgets(buf, sizeof(buf), stdin);
, B$ g; k' j& I6 v J( Z - ret = write(sockfd, buf, sizeof(buf));
% {. F3 s2 C1 p, k7 [6 }( N - if (ret < 0)2 ~2 `5 Q: I! W r4 n) t( H# P" n
- {( Y; J e: O1 R8 A" B
- perror("write");, _5 p; w& J: _7 {- e( T. q2 l7 f
- break;. k) s) x, P9 V$ u4 X" P" z
- }
/ X2 O v! o( X+ L# C - if (strncmp(buf, "quit", 4) == 0)
0 \3 X" c0 u' r - break;
6 u* Y( V& c6 u" k/ h+ } - }
4 S# t! O) R+ s& k0 b+ u. T1 F - close(sockfd);; J0 I/ K5 c3 u; Y4 e! ~
- return 0;" G1 d0 z0 C: u8 v
- }
复制代码
" k' {8 P0 Q a' M
3 n M- {0 r4 b1 ]+ H: r1 p |
|