管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。' a2 B6 a7 a" j* s& k. M
" E- X" P0 R, |
4 `% f; H' |& ?, Tsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. M: O1 Y0 U9 {
- m8 p! d- @% {9 F$ A/ m0 n% O4 ^2 a
/ m: G" V# N; u$ J5 iTCP协议
0 F5 {. f9 @% w" R9 |, I8 O9 {8 PTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
, ~' i7 N% C+ v$ I$ W7 P& m" b
* e; M& e& Y4 C. w6 @8 S/ E) z r7 d5 T8 f. @6 T
关键词:三次握手,可靠,基于字节流。
. y' D# O( H6 v U
: d5 S# m4 l5 D4 W$ a$ o# U* j6 N
! i) u5 U4 e+ D$ _1 G7 N可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
8 K6 ?- ]& U5 l/ C' q
% x _2 W5 r) H o' n5 v6 lTCP服务器端和客户端的运行流程; R0 M# k% W2 J* Y
# P( P: p* h, \9 C; Y
2 Y7 B/ p y' M- W如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
: Y: J0 \+ u% H0 C3 n2 j+ J5 m# S- k7 E9 O3 m2 X, @- S
( R) M x% R/ j; g# L6 [6 f1.创建socket
6 W% t p! x1 _2 K5 e8 G" Z m+ P1 W socket是一个结构体,被创建在内核中
7 D; j4 x4 A' r( V# L" R sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 S/ S" u2 X' m2 \6 p; ?& V/ y1 B2 E9 D2 q) e, h; M
! ?; f3 V- y' L/ q
2.调用bind函数
, ^' |" @! W V3 s 将socket和地址(包括ip、port)绑定。
3 K5 b& F" ]+ r7 H% i8 T 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序% _+ _% s6 [( }1 Q+ U
struct sockaddr_in myaddr; //地址结构体
5 _4 C) s @2 H bind函数
. @- b5 w& Y2 n0 z4 `. _1 i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
; ^& v0 k4 t4 g2 } Z! J" M
% E8 D! g& d1 x& c6 ?+ s3 R. K: p# q: c
3.listen监听,将接收到的客户端连接放入队列/ _: B! T$ F* ~1 J7 g" H! [4 x
listen(sockfd,8) //第二个参数是队列长度2 H( T. T5 }1 l
" o% P0 G, r* Q0 d
$ x$ b! A: m9 T2 h) O3 E, U
4.调用accept函数,从队列获取请求,返回socket描 述符" e [7 g3 l1 Z6 l9 @4 T1 \% W
如果无请求,将会阻塞,直到获得连接! ^+ h, Y5 K9 f7 I" p/ N
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数3 R' Y( ]8 y. z, R: x" {& |( }
8 ~6 r# C% L/ T, ~/ V! f
: K- S3 T2 T3 H4 y; ~* n& h5.调用read/write进行双向通信' P4 P+ g6 j+ s* ?1 y7 K# O3 u- E
0 h" a1 d' Y; r" Y. W, W; k
4 ^" T/ q% g2 b6 ]* P' ^0 M6.关闭accept返回的socket. } X Y/ c: q* r& F4 `
close(scokfd);5 a, p& d8 S% O0 F; R
9 i6 X; H% b0 p' p- V$ j
' ]# O! i3 b5 y
9 G2 Z( e: E! {8 i
# f/ Z1 c J% T! J8 O( m+ }' P下面放出完整代码
& t8 C1 M: [6 g/ j
7 m5 C$ b& `. B' p. s9 D& Z3 L- /*服务器*/
6 K3 U1 T& ^( R# O. d - #include <stdio.h>
8 o( E; x( [7 m6 [2 J& `: [8 x - #include <string.h>
6 a$ _ L+ Y' O& T1 n - #include <stdlib.h>0 g1 D# [5 Y; L n
- #include <strings.h>/ X" u& S/ x& [ ]2 z
- #include <sys/types.h>6 E2 }, \# L9 {( E+ _/ B, i
- #include <sys/socket.h>
' A3 A, u' o3 @1 X! y% Y1 R' h! v& F - #include <arpa/inet.h>+ l: N! q9 l* l9 I8 h
- #include <netinet/in.h>8 b- ?- F4 ]( o. L$ M
- int main()
3 b/ I \6 j, W' G# @3 f - {) S# M& ^4 G5 J' ?
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
6 d& a/ J; X7 X - if (sockfd < 0)
# O- g8 Z3 Y. c - {1 _/ F! W5 ?0 t" k
- perror("socket"); ]0 B# M5 M' S7 Z* |5 @
- return -1;
# P6 v. I, Y/ E- m2 L J - } //创建失败的错误处理" x& F# J! K! y6 k
- printf("socket..............
/ }4 h0 S9 C5 Y$ b; O; S& K3 T4 R! p0 K& F - "); //成功则打印“socket。。。。” H" H/ R* s( P( l+ j" p3 T
-
8 s4 Z8 E4 L; G# k# r, v0 D- E - struct sockaddr_in myaddr; //创建“我的地址”结构体0 v6 q8 K* Z# [6 w; |. W a4 t
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
% I5 P" e* [& E- f1 V0 j - myaddr.sin_family = AF_INET; //选择IPV4地址类型- o, k; ^7 S3 N% O4 x5 x3 ^
- myaddr.sin_port = htons(8888); //选择端口号, t) k5 C. B" k
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
( E2 q2 R( c3 X& L3 \1 a -
; _. F7 t+ o' g( w7 Z - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 I) a. g( S u8 d/ I M
- {
' p3 k# h R2 `+ a - perror("bind");
& F% Y* |/ }( k: k. N2 J - return -1;+ F4 o% L! h( p5 K5 ?$ [3 ?9 ?% a
- }
& f l- ]0 T& E, o/ M - printf("bind..........
9 b8 [# r( K5 C! P- ~ - ");
( b4 ?# o8 V: r" e - 3 e- y8 t. O- r" k7 q
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
, ~8 ? V$ K a" A e( L! e4 T# l - {
0 ~8 M8 A) }4 j# k0 Z - perror("listen");% r) Q3 ^$ t" `: M
- return -1;7 X) e* y h9 p
- }, J. Z2 X. `4 W& S
- printf("listen............
& K% v9 P5 b, u5 [3 o G0 ` - ");% B5 F* k* x z9 `6 {- a
-
3 L# b9 t% k& y4 K - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
/ L+ g. f6 {; E5 I; k$ @ - if (connfd < 0)4 \# w6 g8 t" k, F
- {
' D- R% Q, h9 i1 p! f4 B2 G - perror("accept");$ D0 m p: n. A
- return -1;9 q( r0 O0 x. k9 I9 P1 \4 m/ ], X
- }9 F# u/ t( Q0 P1 q6 Y
- printf("accept..............
; i+ K/ f7 C3 I. t& o3 J - ");
) ~" D1 O9 k) D$ B& Y0 E7 Q - char buf[100];//定义一个数组用来存储接收到的数据- M9 A5 S/ J0 M! R$ O
- int ret;) w, c" ^ _5 {3 ~' i
- while (1)0 @8 Q# T2 {/ _( m- s4 T# p
- {: u/ Q) l! {) A" U J# w. Q
- memset(buf, 0, sizeof(buf));* X$ N2 s: _' ~
- ret = read(connfd, buf, sizeof(buf));
" w! `. j# g1 A# K; F+ t - if (0 > ret)
9 I6 a8 @: [1 v9 @( u& } - {. P6 N+ |& ?; ?
- perror("read");
9 Y! N* B+ e/ B1 i4 D' x4 a - break;4 `, \- r- v p% V2 k
- }//执行while循环读取数据,当
+ g& J. i: w% t0 y# [! i @ - else if (0 == ret) h6 { p; M! ?& Z5 j8 S* z
- {
9 A& F6 E! u7 e2 C' K: x3 }' c - printf("write close!6 E: X/ c' j8 d& b L$ L
- ");
. x, x% w4 k, t5 {8 D- C% K% D - break;
5 Z$ ]6 k9 { f- K; y& \* J - }, t: E) r6 j. P# v/ z' u/ t+ l0 p
- printf("recv: ");) Q3 Y( Z$ k) F" Z5 U' P, @
- fputs(buf, stdout);//打印接收到的数据3 }$ x7 x3 n g S
- }2 ~* S# ]1 z1 X/ H. r( Q4 L
- close(sockfd);//关闭套接字1 A/ O3 m: D; ^+ m, j" {
- close(connfd);//断开连接! V0 f6 q {/ V; K+ P. n
- return 0;! B' D1 S) M$ k7 O* Q1 q
- }
复制代码
! V1 |3 i6 d% f* i# ]* N M5 S9 H% G l$ q+ _
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
& Z% _: C' S* Q - #include <stdio.h>
' C. M) t' Q! x' j3 ? - #include <string.h>( }& \5 s5 K3 T& ~4 M- g. R
- #include <stdlib.h>! p) ^/ N8 Q ^! N6 E( d$ n- e) a
- #include <strings.h>+ E0 d* [( G7 t/ P5 h
- #include <sys/types.h>
) W. ^. {. s! x5 b - #include <sys/socket.h>
# j5 E' M& J, C2 C - #include <netinet/in.h>
) v$ I0 j) { W* U+ }+ T. z - #include <arpa/inet.h># T( C _3 R" K( ^/ s* {
- int main()
5 R4 Y4 ^5 S A6 _5 b7 {5 X8 ^, p" a - {7 a$ }; ?, u) B( h, f4 r
- int sockfd;
. W. H4 r1 Z2 i" s6 C4 j& o - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))); y7 E, A5 t" T) \) H; V v& N
- {
; o5 T2 k: e: q: [; w - perror("socket");
+ |7 P# V/ y, q - return -1;, d/ Q( a: h1 P# k# D! T
- }
( d8 P5 v$ E- l! f - printf("socket...........
1 J2 B1 |* P* k$ p }" N: K$ [ - ");
+ b2 ~; B( C' V5 V- Y - % d4 {7 P2 D: M; o
- struct sockaddr_in srv_addr;
3 C( v* H: X* _, h# E/ W( T/ V - memset(&srv_addr, 0, sizeof(srv_addr));: E, x) O L5 ^: y& W! v) ?
- srv_addr.sin_family = AF_INET;1 m' m& i/ ?( j j7 o
- srv_addr.sin_port = htons(8888);/ i5 H1 N2 {3 l5 ^9 u
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");' o$ j+ H/ i2 s$ X0 f8 m5 Q
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))! n) b5 l! z( A, w) r" p8 Z$ d
- {
8 \5 z4 w9 u6 m8 J( C - perror("connect");
8 b6 Q f" W6 G( K% l2 Z - return -1; //exit //pthread_exit
8 ?3 a5 ^+ i7 m2 u3 D, A - }
6 r; ^# M2 l2 E- x2 ]! E' ]3 v - printf("connect..............+ L, D8 Z! \7 d
- ");5 ~0 `& D* O7 f7 c Z
- char buf[100];
6 v' C' p# u# a1 N; T V - int ret;7 e6 a3 i; M4 U6 l: ~# [( X# \
- while (1)
# T$ o9 g9 I7 G& ]2 e( K: Z+ \ - {# K" j. E8 G# A) j; B, R
- printf("send: ");
. Q) V2 Y, F7 j, ^& L; z! R - fgets(buf, sizeof(buf), stdin);) k3 T2 e/ B& ^0 z; _* r
- ret = write(sockfd, buf, sizeof(buf));* q& E, C6 W& ]' d, q. Z; P
- if (ret < 0)
) l# m, T+ ]& {$ O J - {
3 o4 }9 K: n o% T0 m3 H - perror("write");7 v: U. a7 |2 G [
- break;
1 B7 I( [1 s: {2 T; p - }5 M) O2 u$ d' |/ }0 O$ F& [3 l8 m. {
- if (strncmp(buf, "quit", 4) == 0)7 A9 a7 z+ M- C3 H: [+ o6 `7 d
- break;
( I/ X2 y; b1 _$ o% h - }* s/ c8 T' `3 Z6 j( d% ]
- close(sockfd);
9 s3 H$ }, V- ^ - return 0;) C. V" J, `7 p* z; g
- }
复制代码
- @& j$ {* z. |) C" C1 U5 |0 q3 P" G+ V( X+ r6 q$ L3 k
|
|