管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
1 h' w3 u& y5 Q/ Y6 R
% _" T- x% I% \) x
6 G& `1 O! e, B* qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
0 ~+ h( K' e0 G6 \4 Y7 g7 D; @$ d: }, | W0 ?5 q- a9 ]* o
6 R. y2 k" S+ `. s7 Z0 I$ _
TCP协议
0 A; w: k+ `; F" w* j( STCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; j4 \9 @. z0 ~" s2 x; W/ H0 V4 r1 Y& e! B- J1 a# v
3 f( P) _. L/ V/ d关键词:三次握手,可靠,基于字节流。! t% Z% z N& {- p) W r$ l
( `+ v$ ^! p: Q* W h3 l
/ R) w& ]+ t/ y% [可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
8 |7 i' I. ^$ p0 t# w
0 C4 f( X+ B, O" ~ l" [ d e8 |* ^
TCP服务器端和客户端的运行流程# \/ L; ^) f, R$ r
) a& p1 c1 q1 ~( x$ Y2 S0 L; v% K: [6 _
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
. \3 h/ y8 d8 e5 W& w7 P" v F B2 s/ o$ }2 c1 I
' O J( v$ H0 ]# ` E8 C. ^
1.创建socket& @0 K" \% q3 ?7 i8 G
socket是一个结构体,被创建在内核中
6 c7 p& G% V) \1 z+ Z sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 R, ], @2 B# O/ ]! K6 o. C. C2 f7 Q. `4 [
w1 c3 A1 S* U4 @$ R2.调用bind函数
, ^6 K0 ~; `0 } 将socket和地址(包括ip、port)绑定。- S' u% i$ S( \4 e+ I, {+ N
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
g% N1 Y' j9 @1 Y' W4 u) z struct sockaddr_in myaddr; //地址结构体
9 B' T4 a2 K4 o0 C9 B' t) C bind函数
# V r+ p4 O/ G% `6 U1 b bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% ?. o" V5 ]& ^* O' h8 I
3 P/ r M7 @% v& F: I( L; R$ s7 x( ?* u2 I
3.listen监听,将接收到的客户端连接放入队列 d8 i0 M; D& [
listen(sockfd,8) //第二个参数是队列长度/ [- u. i! W; D3 I* F- x
+ X( m7 c" }! O
6 Z2 ]2 C) F a, y2 }
4.调用accept函数,从队列获取请求,返回socket描 述符6 ]8 g# q. u1 }
如果无请求,将会阻塞,直到获得连接
3 s: p" z! ]1 k& ~) g int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
) p: t! @7 O5 D2 \3 d- j7 J! j, w" h3 N* D% M7 J- `* S0 k5 s
- [: S; ]0 x8 Q
5.调用read/write进行双向通信- x4 V( m. v2 }5 f. m
; _5 }+ g# B. a, n, G ]8 O4 a! d/ P4 H! I2 `: b
6.关闭accept返回的socket9 ]% N, a8 k4 O( C) r% W# y
close(scokfd);- D/ v0 A. u# l, s& O+ g
* g" |7 K( k" V: V& B. J( e* J; ]+ |; J
# \/ w @" B/ n, F) K! R
& c/ E7 z& F+ U4 x2 `下面放出完整代码
: [. D' A! k, a) y! Z8 q8 Y" d7 o; e1 s) i
- /*服务器*/
& W- r9 B N1 w- s5 \ - #include <stdio.h>
3 E8 }( s( `/ N, |- \4 a - #include <string.h>
: X4 I- }" }; f h& J1 B3 g- J - #include <stdlib.h>
( f; H u' J- |+ J4 ]# t5 R - #include <strings.h>$ T3 o) u) ^0 Q* N7 ?
- #include <sys/types.h>- s5 H( Q8 i0 @/ o
- #include <sys/socket.h>
# E& V3 n t! Y3 r. K5 d - #include <arpa/inet.h>
- [% u$ q2 o) ?+ r, V - #include <netinet/in.h>! o4 K# t, l- @$ X8 S& j
- int main(): {3 E1 V* {4 D4 C5 I' A2 a+ r
- {
! u) ^% w( B0 t+ }0 j - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
9 X* t7 R3 n' F7 k% {9 x# i - if (sockfd < 0)
; z9 ]4 {# ^6 b/ T - {
; Q- w' ]0 ]# M! }4 P9 e* o - perror("socket");
4 _/ F9 u' \$ U* i/ ]* P/ _! s: L3 _ - return -1;
3 y! J% E) v0 O, ]" T - } //创建失败的错误处理4 C, m+ \4 @5 v; |- I5 I
- printf("socket..............& o( a4 j0 }. ], Q( t
- "); //成功则打印“socket。。。。”
% d: T. p8 H& |9 H -
r) V* u) k& q1 ` - struct sockaddr_in myaddr; //创建“我的地址”结构体
: h* T$ X3 y0 [: b - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 @1 d8 d3 L) a7 x+ t7 ]
- myaddr.sin_family = AF_INET; //选择IPV4地址类型1 i# I1 J4 d/ j1 ^
- myaddr.sin_port = htons(8888); //选择端口号
^& F9 f0 e) _! W | - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
0 \% {$ `5 {: m! m - 4 m* H A9 x; B( _' i& W
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
( Q, x" f9 z; E- P" q$ D9 w. k - {
: n9 f# I5 D& H. o - perror("bind");
3 o# |; O8 u4 E% o7 b# e: K% Q - return -1;7 Z, c% Y6 Y6 W. F2 W1 b+ S6 {
- }: G' r. r) G2 U6 L1 H1 i( ^( @
- printf("bind..........$ {9 I( g2 f1 h/ J4 V& R
- ");
% F" S5 w9 L; _/ F, J2 {% E- g# K - % m. L+ A( y& O, Z1 U1 ?
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听) u( t+ V! x; A: G( K$ ~5 K- w5 w: F
- {( Y) R1 T% Q" b8 s S% V( V+ L
- perror("listen");
# V( ]$ k' t* i; R$ r9 W - return -1;
2 J$ A- G' t) U! ~0 v% w5 ], f - }$ p& V5 ]9 d+ [" q- r: U
- printf("listen............
6 j2 w; W3 B7 i R' S - ");
" ~+ `* S- E) q3 R -
: j6 c% Y6 u1 D" r$ E# a g* [, E - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
3 X( z" y5 S9 F, j# a - if (connfd < 0)
/ F0 q" j7 i$ N: u7 D - {, Q8 C( T* h2 @$ S
- perror("accept");
- z9 {" t% B2 x" D4 M! q5 O7 q - return -1;- }+ a) i. L% a( U2 P
- }) s3 a& P0 o) d% X* d4 H* |& X, C. W
- printf("accept..............4 I) d5 q# N6 [9 F2 _. q1 ]1 x
- ");
8 N+ u" X) S) O" F9 L7 c* k - char buf[100];//定义一个数组用来存储接收到的数据; ~: M# \/ _- |' K D5 I
- int ret;! M8 W8 X1 X( t$ W# [: y! F
- while (1)( ?$ ^# N3 s; r- o+ D
- {# O; B- c( h+ |! J/ t
- memset(buf, 0, sizeof(buf));
& V9 c! [! N7 d9 X* w2 ~1 y& S - ret = read(connfd, buf, sizeof(buf));, i. z7 @" w" g; N2 M) u, U
- if (0 > ret). I4 }2 T+ g* E" V2 K U
- {
/ e- ^. { v) i4 x( N - perror("read");
% b o; o9 H& i - break;' P' }1 I- k4 b& n6 b
- }//执行while循环读取数据,当* H+ b- b' u# _
- else if (0 == ret)
# I. q# t5 N. H* |5 y& ]6 | - {
! b, w7 z/ K3 g, c& u. P - printf("write close!' b$ S5 \/ Y. J: `" L) E& ?
- ");
3 _7 T, V) Z0 ~2 z - break;
4 s" L7 b6 \1 G& u+ g - }
% C g- ]% ~+ a: I - printf("recv: ");
: H8 s2 w* T) a" {; o2 N9 j$ m - fputs(buf, stdout);//打印接收到的数据
' Q1 C) C% P; e1 Q% n2 `& A/ q3 j - }
8 z* c! F( a& }6 e& T) i% c - close(sockfd);//关闭套接字
0 v) T x9 v5 v6 C - close(connfd);//断开连接
3 p8 `; t) C7 f/ o - return 0;$ A8 T5 s6 j/ s8 o9 a& d, O$ D
- }
复制代码 & k1 r3 Y! N: J( |; l
8 j& M; C) _; n5 z6 g+ B3 X
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
. z8 i* ]1 C9 i - #include <stdio.h>
, Z% u( i/ E E$ @/ X - #include <string.h>
]% m; |' d# K8 z" m0 O7 g% E. c - #include <stdlib.h>2 ~/ F3 v8 B& j$ \% S+ [
- #include <strings.h>$ S6 g0 e. f! S8 S5 `
- #include <sys/types.h>2 @! ]4 k" b9 h/ j" L
- #include <sys/socket.h>/ v$ q5 x7 ~% |0 [* {
- #include <netinet/in.h>& Q) R9 J0 k6 D1 y, k! }+ m4 q* f
- #include <arpa/inet.h>
3 K6 W) k( O' z' T) F# ?9 w l - int main()
7 e( v0 `' w2 g- ^: S q2 i - {# ]) E3 M& j& ]0 P
- int sockfd;
) p3 q9 s3 j9 Y* ?7 `; @ - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))" ?4 _# {, q- ]* y/ P
- {! Q3 @9 @( }: T: g& y1 k" d
- perror("socket");
" v$ F$ X3 y' j - return -1;, t( e: ?6 k9 a* c# `
- }* J, D6 y0 k' O0 |! o
- printf("socket...........
# ^$ Y% F) e9 A3 p - ");% Q1 |# a2 V1 D8 B1 U5 ]
-
5 m3 p; o$ y3 x - struct sockaddr_in srv_addr;
* ]7 j+ }. n/ I, E i - memset(&srv_addr, 0, sizeof(srv_addr));* c5 `0 W$ D" P0 e2 E% E% D' P
- srv_addr.sin_family = AF_INET;
/ P0 Y) w. q$ ~% \ n' m6 K - srv_addr.sin_port = htons(8888);, O B2 p- q) B
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");( m% }1 u9 \0 b, S& r* W
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
6 P" q/ i# \; b; k - {
" G: ^. M7 u* w9 P& i - perror("connect");
4 p- B1 [/ V% s' n6 `, C3 @/ f - return -1; //exit //pthread_exit
( x5 r1 O4 y+ ~3 U# i9 R8 l' Q - }" N7 E) o" G$ R: E/ l
- printf("connect..............
4 h1 s9 R, D! `4 Z3 z( N - ");* v& q/ k( R' A3 [. Q+ i. [
- char buf[100];
( o Y- p) q1 w- V( K2 j# B - int ret;# P* H: n0 I- m' s( \: |( a# {: z
- while (1)) u+ k/ i( O1 S+ T1 V
- {0 v. M" K4 ]1 Y7 @" [) h$ Q
- printf("send: ");: |% }9 w' H, G$ f7 q4 J
- fgets(buf, sizeof(buf), stdin);
( W5 A5 U9 h, V; H+ E7 U) R - ret = write(sockfd, buf, sizeof(buf));
( W( k- U8 _7 |! s/ ? - if (ret < 0)& p2 ]! q9 c$ n
- {% T" K2 S0 P" D: ^7 K9 _2 ?" w
- perror("write");
4 U& J/ Y3 Z7 ? - break;
4 j4 l4 Y. r: E5 W3 }" m - }
: X3 {* ?4 a- C, H) [2 I - if (strncmp(buf, "quit", 4) == 0)2 N. o# t: u- p# j
- break;7 F0 ]+ l" p" t) C# G5 j# i
- }
; [2 X* D9 L* m# [" m! {3 K4 X2 l; p - close(sockfd);0 |+ F3 u1 w) f. @' [4 c' e9 q
- return 0;8 }6 p% L. B* g
- }
复制代码 # T- ]/ V5 H' _$ \2 Z
4 Z6 J7 N7 F/ {' t3 t
|
|