管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。: K7 R9 T( ~; I6 ^% l: E2 Q
8 x6 ~0 J. f$ K
( O3 J3 U! X" s$ Z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
# y9 ^& N9 P. ^' L+ L" ^4 j) E- Z/ T% D5 e( C( u; d
. N/ e9 h) m/ _' n3 v% }TCP协议
: D; h) }( `# ^/ C$ ]. N; N: FTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。5 s# W+ q, K2 a- n9 R7 \
- a9 i7 q" q% w- S+ [ Z! y& D r/ [4 j }
关键词:三次握手,可靠,基于字节流。+ k0 }( L1 H B/ F! }! |+ F
" R* W3 Q$ M/ V8 j& t! A+ J. M$ z! d& N: C4 {1 X' b6 _
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& E/ @: E* m# D& Y) W( D
! a: |+ j8 p" u, V }6 p# UTCP服务器端和客户端的运行流程
+ [. D2 G0 f% s' @6 M2 P$ ?5 S
7 z5 b+ B& a7 A9 ]! I3 ~' W% H# `7 ?8 }. c
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ I9 J% m* U& H0 e4 x" X0 I
9 x) K% K" v9 D( _# A* T4 ?
: u+ h' k" s& _+ B3 C1.创建socket) ]6 a! ]; M4 T
socket是一个结构体,被创建在内核中
- e) X$ C, O) ~! a" E: P0 @ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ d" ]) L4 N: V9 H" g% p$ f
/ g+ U. S% X, l s! @3 _/ P( L7 X I6 f
2.调用bind函数6 `8 ]0 ~ f0 I" @6 ?
将socket和地址(包括ip、port)绑定。, Z6 ]9 f6 @% G! p, w& r
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
. a, f% I$ Z% o struct sockaddr_in myaddr; //地址结构体8 x; b( B7 Y: L
bind函数
8 Y9 D' g. I5 E ? K0 N6 i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% |7 t9 M( a. z& M1 | b t& {/ m# ?3 d( I3 X* w( @1 P
5 N" B# g0 H5 }2 g" Y
3.listen监听,将接收到的客户端连接放入队列0 d% q. L7 \$ s
listen(sockfd,8) //第二个参数是队列长度) ], q7 ~+ M7 F7 v: P4 G0 B
) @) M% W+ D2 c) ]3 ^/ }; d
k" Y: z" s& v+ c! a
4.调用accept函数,从队列获取请求,返回socket描 述符- P& B" Y9 y1 @$ z: n" s
如果无请求,将会阻塞,直到获得连接
5 p* H& ?7 C$ d0 p! ` int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* F6 ~9 a( o F1 a
, u& F( s# I8 B) l8 t' V) E Z3 r# G. z K! @
5.调用read/write进行双向通信
: \. _8 @% v' R# M
$ u* ~2 P4 D0 u, y" Y" b* M' E: H7 g. z n
6.关闭accept返回的socket
1 p+ Z9 I, k, U# a6 H! l close(scokfd);. `5 E6 O- F; O8 t
( v3 z- [7 |. a( E- `( _. C2 O: E) D( o2 K' C
$ x1 T" d2 S) n0 P# K
8 x( m* ~. k/ P; q" F" V下面放出完整代码1 }0 J# B4 J3 k, ~0 z( t/ y4 p
9 |4 p, b4 ?" t. E+ N/ U
- /*服务器*/4 L( w% ~. B5 z" r9 G
- #include <stdio.h> H0 g) r3 I7 r# x9 K8 B# o
- #include <string.h>
% ^. S' e" ]' o+ M. X+ z: I - #include <stdlib.h>
9 N% w2 i4 }/ N+ I! k' u0 S A - #include <strings.h> U. \9 m8 X$ S) p! w
- #include <sys/types.h>
' s& T0 z4 Z6 l `( x - #include <sys/socket.h>* j, Y) K* q. Q% s1 `5 c
- #include <arpa/inet.h>
: u H) Y4 T) m" R- _& l% G - #include <netinet/in.h>. {* Q; F- m( J* T9 ^# G' d2 ?
- int main()# Z9 F$ w& `1 U( W" A0 o
- {
% `. R% ~, n" G5 j# I/ p - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
6 ]2 L: h6 C; _ - if (sockfd < 0)* M$ @% O- z* }% N4 Y, Q
- {
, C! p4 N: A$ x* o* z d: e7 N - perror("socket");
! V- n4 w# M% O% } - return -1;9 {% h) ^" z. X6 V
- } //创建失败的错误处理: E, `3 g* g+ ?! Q3 @
- printf("socket..............
5 D. ], t/ B. E2 `6 m7 X - "); //成功则打印“socket。。。。”" z3 ^7 ?6 \: S" M% X
-
$ A0 E# E/ f3 O! U2 i/ l2 r" b. u6 q - struct sockaddr_in myaddr; //创建“我的地址”结构体
9 u Q/ y: N4 `3 j( n7 B: Q/ z - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)2 }: u8 R9 I/ j. B/ {. B/ R" Q
- myaddr.sin_family = AF_INET; //选择IPV4地址类型! [, I' d, l8 C: U. {5 q0 k( ]
- myaddr.sin_port = htons(8888); //选择端口号$ G7 l! ?5 R1 {( w$ H$ [
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址! X1 q6 d) h# }3 V! R) I/ S- I
- / l' B. i% ]6 ^ n: O
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
7 C& K) G# \/ J' B5 c, c - {7 b9 {5 Q+ `7 ~; t7 o6 ~' C
- perror("bind");
0 N) a9 H$ u0 T* x+ H1 t) q - return -1;* ^1 w8 q) d! @! C: ^' p/ Q
- }' t6 P# d! e5 {6 C: g2 i X. r
- printf("bind........../ Y( m5 l' D+ G; }3 s' g
- ");( z) o7 R( A$ R. ^) c3 @% e
- % T8 X( W9 @- [
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
7 v) F% a! E1 H: c. ? @8 \ - {
+ C9 e% N0 G4 ~$ N2 h6 q - perror("listen");
5 I, ]" [9 n7 E( O W - return -1;+ D- {( K; Q( _7 h
- }! r# E* x0 Z1 h! c/ f
- printf("listen............
8 S* J: k" r7 ]) d9 I( Y" L - ");
5 a% o( B! I) `9 E -
9 t# q& \( q9 @) O( d: z - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
6 _2 Y0 I" F$ K1 A8 H - if (connfd < 0)+ V; w5 R9 W0 {7 i) q4 w/ Z
- {) T5 W5 L8 D$ `% d8 ]( q
- perror("accept");' {2 i: `2 W3 M/ j s
- return -1;2 M) q Q: J0 b }& W$ _
- }
) ~% I( C- _$ ~) S. K - printf("accept..............
, }+ d) X6 E4 ^; Y! @) C - ");$ ]3 y1 v9 R; b0 \+ l& b) r# k, f
- char buf[100];//定义一个数组用来存储接收到的数据
: V! U# a o, j& N) x- [ - int ret;
( x3 s) G+ k0 z/ E6 x3 E* [2 D - while (1)
0 p5 W$ x" K9 p$ I! f. V - {1 o. O5 q/ v3 O/ W
- memset(buf, 0, sizeof(buf));0 R6 v& j6 ]; `* A$ X' G
- ret = read(connfd, buf, sizeof(buf));: |! V& F0 k8 W
- if (0 > ret)
) x* v* o$ ~+ R% ` - {
' C# t3 L: P' s2 _6 M - perror("read");
* J' E$ b; o* d" i2 O4 @6 q | - break;
5 W" z! M0 V* X0 B ~ - }//执行while循环读取数据,当
+ g* j8 I1 ^9 [+ J. O+ K9 j6 g - else if (0 == ret)
9 |6 N M M$ k - {
1 @* Q$ u; S! \ - printf("write close!: l9 G! ^' k6 D$ s
- ");
& e _5 V3 A- E7 P - break;% S5 v Z, l! W7 J0 S$ l
- }. p/ @3 i6 J9 e9 ] u. _- \
- printf("recv: ");
; U3 z/ l8 {' z# F) O6 `2 J - fputs(buf, stdout);//打印接收到的数据
4 L: J# u; r2 J1 m( |6 J - }
0 I P$ z4 T, J7 T' Q: q - close(sockfd);//关闭套接字
* n$ U( h* K# y. T - close(connfd);//断开连接! \8 t$ f6 b# G/ x4 | N Z7 x$ e
- return 0;
?/ F' P: O" A* J, R - }
复制代码 " b9 j: ?! b4 E! Q! u% \' c
# |4 {2 U; \' D" m' A# _8 f2 s8 D, N
- /*客户端*/(具体功能和服务器一样,所以不再加注释)6 E, l* H& `6 ]9 q( M( U7 o" M
- #include <stdio.h>0 d D- Z% f9 \+ P5 @: {
- #include <string.h># | p: s$ e1 P6 X/ O& s
- #include <stdlib.h>2 B: w0 C |, w, W5 Q+ @: g
- #include <strings.h>; i/ O) M4 e8 b2 @/ ]! m6 u9 g Z0 k5 Z
- #include <sys/types.h>: G/ y/ v- r1 F# B, K
- #include <sys/socket.h>
* O8 x! U n t1 w9 w# l - #include <netinet/in.h>
, H* n) a! D6 Q2 o d* n1 N - #include <arpa/inet.h>
; M- J W" L8 L2 f; }1 S2 W9 m - int main(), K7 s; n5 ]* P
- {
& h9 D7 u/ ~$ D% ?8 w. w - int sockfd;
]5 k, H2 m) _1 F7 Z1 \0 D' E4 k - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) P6 n& z; p& e- i6 S
- {
5 }; E5 U1 g6 t! F: |( w - perror("socket");
5 |* A) y7 u7 G- [* z# [ - return -1;
+ z/ ?2 \9 k: P7 N J - }% K+ ^, C$ Q& n; m
- printf("socket...........) u* m; ^& w1 {8 o) N
- ");
9 j" i m/ p1 ` - . m, t" a4 r/ }2 p1 m
- struct sockaddr_in srv_addr;8 {% n; Z# O0 Z( `( w3 B
- memset(&srv_addr, 0, sizeof(srv_addr));# e" g9 c2 N6 e! v* C
- srv_addr.sin_family = AF_INET;
6 x& I# R7 X) K0 j7 a$ ^. P - srv_addr.sin_port = htons(8888);# t! z) }5 V: ]* n
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
$ r, d' j) |" h, \5 X- ` - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
7 U$ q; K: Q3 A9 z0 R3 I; M - {
c' S `1 n- M3 d! O$ F - perror("connect");
; V4 f, d& D1 q: M: N' ^ - return -1; //exit //pthread_exit g# F+ K3 b1 x7 f# A
- }$ Q6 c4 N1 Z1 w9 r1 T
- printf("connect..............( B) \- `! m8 Z5 k w; K0 Y
- ");: [. @6 `5 z% @) i" K* q$ G
- char buf[100];* B, j- h, C3 j
- int ret;7 J2 q. n, r- S& O' ^" v7 V
- while (1)
+ i" w' p. V/ X - {
7 _: C6 g3 w, H* K) f0 k# [ - printf("send: ");
0 b; Y) T& @/ J) e4 D+ d - fgets(buf, sizeof(buf), stdin);& a9 d9 m9 }; L! R
- ret = write(sockfd, buf, sizeof(buf));0 U% a1 | B+ M2 a' r
- if (ret < 0)+ e0 R# w# y8 H7 a$ j; }
- {) {2 {' e5 \$ \# n# |6 d0 Q2 i
- perror("write");# Q' m8 K& P$ P2 L7 ^/ T5 M
- break;5 o% F: O9 X$ t4 T$ B' z
- }
( I6 t6 m$ d8 a S+ V0 Q/ B - if (strncmp(buf, "quit", 4) == 0)# k( U7 D. _! e8 e( F6 ?$ t+ r+ Q
- break;) b6 w. f' }# L* n
- }9 l) w9 z k% n. `/ ^; _
- close(sockfd);& S7 ~! Y$ b' b& u2 @4 s9 Y
- return 0;1 F( J# J$ I2 J* v) t3 O6 p
- }
复制代码 ! ]7 @9 ]" F6 O- \% q% a+ P
. Y' U1 D( M) `+ X8 D |
|