管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。% Q' l+ X% k" P% a
2 t/ y# X3 N4 z Q( i: c- @
5 c3 D e6 S! G. F& A9 F" zsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' P/ y% U X( V9 h& _
0 X/ t6 [' {7 c, Y4 [6 z
7 f1 b/ l& P5 ^8 W0 J8 F
TCP协议* {# |: @& L+ R: j
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
6 H5 I3 M8 |, ?+ c6 |2 j
$ L0 H G* A6 H6 j; p$ H2 d$ W
7 i- Z5 K9 ^" P3 l关键词:三次握手,可靠,基于字节流。. Q! x0 p9 o7 C2 I3 G
, c& \: E5 x$ F+ @
8 R) G3 @% w6 E7 \
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
% i+ t" X+ {3 S
9 k8 v" s) f. j
TCP服务器端和客户端的运行流程5 {1 `, Y, V) n' I- ^9 J
5 l& V/ b) P, c: K. c* v( H
1 `% |5 @/ b- [9 ~
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?: P1 ^$ ]& ]4 y. o
* B, y6 ]; R Z( Q. n E i0 }2 D# s. r* Q4 i
1.创建socket( v7 ~) S3 q% @9 T6 `
socket是一个结构体,被创建在内核中
3 q0 S8 c5 i6 H sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
" ]- e$ S/ w) F: m& h, `
4 M7 d- k- F# c; b+ i0 \7 Q# J6 _# _# _6 u6 B9 J, i
2.调用bind函数3 ], z) d/ L/ j2 W, A% X
将socket和地址(包括ip、port)绑定。
# |/ G) }3 S4 q' m 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
: ^! y% X2 g0 T" [- Q; i struct sockaddr_in myaddr; //地址结构体9 @, C6 S" \- c! s+ U" j
bind函数, _/ h) X2 O _+ ? S
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 v6 }0 @( X" `* r \( K2 Q
5 _" o8 w5 p# Q3 M( S; P# ]! p
- B( S% {" ^) [5 u/ @& K
3.listen监听,将接收到的客户端连接放入队列
) E$ I+ |' v+ ~6 m6 } listen(sockfd,8) //第二个参数是队列长度4 D7 Q: a& s7 H( M; r. O/ U
1 R8 f% R6 q, ]* u) k/ a, M3 |2 X m: Q. K7 W
4.调用accept函数,从队列获取请求,返回socket描 述符
: U0 X$ O3 g. T( ]; g 如果无请求,将会阻塞,直到获得连接
9 ~" U8 A/ K+ R f7 f" o% \ int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
# r4 R. |$ W, j+ i) a) e* ^5 L4 [; f5 z) K- I4 ]
+ j7 |: s& x2 |) @8 @ v- h" V5.调用read/write进行双向通信
) N* `. G1 t/ y$ Y3 b# B
/ b$ s% m m" T( c
! Q5 Y$ K3 h8 ]* y6.关闭accept返回的socket* ^" i4 T* o" R9 l/ F
close(scokfd);$ i% y# D3 W3 d' i$ F" }0 c
- t6 o: ?; E0 e i; i
4 a% k L' ]0 B+ H
* k# [. X% B% u, u5 f; @
0 Z# j8 P5 [* H6 R; ]
下面放出完整代码
* x2 K% u( F3 l [0 g) _3 p6 x* F+ a/ o
- /*服务器*/
3 ]( D f: m; | - #include <stdio.h>9 D: u! F8 M9 `+ e, W+ K
- #include <string.h>4 u7 ~ c$ @% f% n
- #include <stdlib.h>! ?- z$ }% M6 X+ j" z
- #include <strings.h>) c. {. b0 O& X5 O& a
- #include <sys/types.h>: p+ t" T0 w1 Y: W
- #include <sys/socket.h>
A' ?8 I5 I9 R* c, L/ d' ?+ e - #include <arpa/inet.h>
1 v- W4 F% k; x7 w - #include <netinet/in.h>0 m8 u! z4 d& K: U$ ?
- int main()8 I: @# J% O4 v5 r
- {+ H4 ^; O# y) J7 q, h* F5 X
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# L- P }& @, ~3 T4 f! s: |% ]# f
- if (sockfd < 0)
3 G. o& e8 e, E, e& b- g - {- s; J" R m# O2 O& X, b/ N3 s
- perror("socket");, `: P; g- i% b& J
- return -1;
, ~, n1 J' @, r/ A6 u3 A/ |- H* m - } //创建失败的错误处理
3 O8 _/ I: L9 m2 T# e0 l - printf("socket..............
- l* G0 F8 {( |: i - "); //成功则打印“socket。。。。”
$ y1 V* v# A2 h( |* Y -
_$ q+ U" I J6 i$ D6 N - struct sockaddr_in myaddr; //创建“我的地址”结构体
: V2 c, |9 x5 ]- D# [( f0 s2 a: I - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)4 M% m2 D2 d6 }, X* d- i
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
, I+ Z2 v, \" k( ?# e3 A5 X( T" [ - myaddr.sin_port = htons(8888); //选择端口号
8 @, g7 B* V( i& r/ {5 F+ f* z* N$ Z5 h - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
! b7 t `/ } t4 [ - 7 Q3 K" L: v2 T Q
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 r7 I$ n5 a$ g" G1 G
- {' r5 E; t; E- V! B
- perror("bind");7 a7 G# o* \0 h8 Q; I. B" ]) @
- return -1;5 L6 K1 D: c& G- `, e
- }
, h4 b3 q% F9 W) A" }! q8 w2 Q6 J# e - printf("bind..........
' F, X' B4 |7 A y4 C - ");
$ k o: H4 X. M0 |) K& u - 7 D X3 e1 m1 W, n" a
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听+ K. \! E$ Q) I+ R' _
- {
, b# Q# H5 T6 \8 D - perror("listen");+ c3 e" M3 d& x& h
- return -1;
: j3 r% e& m, W - }
1 [7 \ X. P3 f - printf("listen............4 d5 i1 z" c v; A( \
- ");
8 w0 v: m4 }- w5 X+ ` M -
) l: v6 X' d) g5 e* H `0 v9 ] - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求2 P+ }. J8 D0 j- }
- if (connfd < 0): f3 F$ [0 X3 ]- D7 d1 W1 `) I
- {+ Z' j: m. Z2 U6 w7 z2 Q; D/ T) D
- perror("accept");5 [+ L) P7 X* \# W9 O: h
- return -1;
! C- H) w# s1 U* [, p: ?+ T - }* s: W: z7 l1 I8 f) F( u5 L: ^
- printf("accept.............." K8 d* Q# ^( A3 v t/ k0 y
- ");
7 {$ Z# v+ a& U0 w - char buf[100];//定义一个数组用来存储接收到的数据 V2 [ g7 y0 T+ B2 m
- int ret;1 k' Y' K! C* x# j) g7 K1 w- {
- while (1)9 p( X9 ^1 N. L
- {( e% }6 e7 v1 Z1 o: M, m ^5 a
- memset(buf, 0, sizeof(buf));
$ z0 R& Q2 S1 Z) g) _9 L - ret = read(connfd, buf, sizeof(buf));$ M6 }+ @+ r8 h2 Q7 R
- if (0 > ret)
+ E# q+ v' C- ~. C - {
/ T. n% Y! ?+ R# h* N5 k0 u - perror("read");' F/ P4 V E7 @8 ?+ E x$ l
- break;
( M3 N. y3 e! J$ J. z - }//执行while循环读取数据,当7 O7 Y% S6 j; y( ~
- else if (0 == ret)& y+ p+ w5 K/ E
- { h# B* ?; p1 U8 w. ?# Y2 c
- printf("write close!! w3 f1 X2 g( `9 v* } \
- ");1 J2 R; K7 l6 Z! Y( R2 J
- break;4 w/ t0 o2 `& Q, h% S
- }
& K/ l8 n; @: h! d! h - printf("recv: "); d' Y& J5 s# I' f2 |! a
- fputs(buf, stdout);//打印接收到的数据
7 j. E/ Q5 H7 b+ ^" l2 |6 n }, e* C7 v* U - }
* _( j+ S1 @4 `3 h - close(sockfd);//关闭套接字5 B/ Q/ d4 h$ ]3 T4 a5 E. Q
- close(connfd);//断开连接( q0 `% q( M0 w- T+ P" r" o5 w
- return 0;1 Y+ ^0 x- _: r! b! `- K
- }
复制代码 ) W6 V( o& @2 d, g
; ~0 u6 k: T5 x% |* k2 g! Z0 O+ D- /*客户端*/(具体功能和服务器一样,所以不再加注释)
3 q) y, y7 Q$ ` \4 | - #include <stdio.h>4 L* Q9 r6 u' z. B( }" l5 u
- #include <string.h>& p8 Y1 b. J0 S0 q$ `
- #include <stdlib.h>
; r L6 G4 r$ i6 P - #include <strings.h>
6 Y8 r: n$ M+ E; ~; ] - #include <sys/types.h>. ^. c, q* p1 N; {- m- x
- #include <sys/socket.h>, T. r4 O# O4 B
- #include <netinet/in.h>% A9 b% M+ \/ |$ Y7 k+ y4 i
- #include <arpa/inet.h>
8 `- O" ]8 w$ Y- { - int main(). ]4 j8 g6 T" p7 ^
- {
+ m4 m3 ^/ l: O( F2 o - int sockfd;
& K7 D9 V8 W4 q- @, \ - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' {1 E8 v7 g8 q" |
- {3 ~7 @- G* b- j. `' i% C- @ a1 @
- perror("socket");8 j/ l$ z. _* X3 J" j0 h/ e- o0 H5 p
- return -1;
$ g7 X1 y8 D0 z/ O' W& T- _ - }$ H1 l9 P8 K; _9 h* k; a1 }
- printf("socket...........' n! A. k$ Z/ n5 S
- ");
6 N* @2 [8 x+ O" T; ] -
9 A2 F/ a0 E( T/ c$ f: T - struct sockaddr_in srv_addr;- m# x! C$ B; ^4 H7 A
- memset(&srv_addr, 0, sizeof(srv_addr));! n. m/ S* k* Q
- srv_addr.sin_family = AF_INET;4 a8 n: M3 K; w, H! E
- srv_addr.sin_port = htons(8888);
/ O" a) x6 ]4 |5 T, l - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");# u" k, R3 d w6 Y' t
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))- A. g3 N- A6 w4 y5 W4 W5 l
- {& I5 o8 Z$ n; q" M# R/ o1 o+ u
- perror("connect");
$ t0 ?( m5 g3 Y5 q& F; L- t - return -1; //exit //pthread_exit6 k# W! \" ~' T7 s0 n9 t
- }
* B8 b) p1 j: E - printf("connect..............
9 l1 x! ]& {4 r" @( | - ");
; R( J0 I( @! y - char buf[100];. G ]: G8 U- l9 o4 B0 _( m
- int ret;' l6 r# X, h# ^0 Q# B6 l# u
- while (1)
( ^; N# C2 m, x: H- w$ @ - {
: r8 n, e8 n5 a7 Y2 @ - printf("send: ");
) b' ^- l# n% O/ s ` - fgets(buf, sizeof(buf), stdin);
0 v5 S. Y- E2 G& W - ret = write(sockfd, buf, sizeof(buf));
9 |$ J$ m) n7 Y# J; r. k - if (ret < 0)
5 |* n Q0 }1 K) {) p - {" a. t: w6 |6 f j( B: m% ]: q
- perror("write");3 ~' T, x, U4 Y, {- [
- break;
& j3 ?9 J$ U) b1 }4 P" P - }: R8 b2 _# R0 _" u, l0 x
- if (strncmp(buf, "quit", 4) == 0)0 G6 k5 q" y0 l D
- break;
7 b9 ]# h! C8 a" @/ n5 w6 X. H- r - }. S8 T# N0 c; c+ p
- close(sockfd);: r% O' U+ ~5 y
- return 0;
9 g6 N p2 X% @- x+ S: }; I, X - }
复制代码
8 o6 I6 h' X9 [7 ]" g; l
- }( _; i y( ~) H% {; J |
|