管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
' V2 L$ P4 ^5 T5 A* e: X; h* ~
* g; `5 c" |# f1 o7 Z9 }8 b3 h5 K, O( h, d$ Y
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 t; j6 s3 D) K0 A& W# s3 _
3 H& r. Z+ @( w4 K0 v1 r+ C
; `9 H4 X' z' r) q! S* RTCP协议
# J/ ?$ B' v3 I4 a6 HTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
2 |7 I9 q( E( Q9 p7 L; b, }: F
. m% W/ y6 [! ]3 m
3 T: n# C) k8 F. F7 I3 k4 A1 S& a5 x关键词:三次握手,可靠,基于字节流。, r# |) `1 v4 [* p% r
+ B! I3 w& K- w0 a% T* H) O" j& e- P
- `* [* A3 Z1 Z7 B9 c
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; P9 C& V# \6 E+ i4 v' K: N- f
/ [8 [7 _1 X* i) p
TCP服务器端和客户端的运行流程7 I$ i0 F; u/ q+ [
; b/ E" x8 Y- i; i4 Q6 |& T1 d* M, r( y9 D S
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 i9 A) O. K4 U* s% u0 E; G: O& C4 ^* i' a+ a; h
- _2 r* Z) U7 g' F& w0 S k
1.创建socket+ L. Y6 b. i1 j( q
socket是一个结构体,被创建在内核中( Y3 g0 i9 M( I6 d" {# P- j. m( V5 M
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议% b, Y% l* N; B, }0 T
, D: R( ?2 F8 M6 |- B; N l- o0 m2 j) N* w5 H
2.调用bind函数. K2 j. P5 m7 k! D
将socket和地址(包括ip、port)绑定。" S% M u9 Y% Z2 L+ h/ a- H+ _
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序' y A/ A5 N# |1 x+ m+ {- u/ \/ s3 A
struct sockaddr_in myaddr; //地址结构体' M3 w5 O; x' F7 C% l
bind函数. q" V* [% n- J, P% T/ n4 r
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))) f3 X& D" f: @( Z y
( }, L7 G3 H' R
* d ?. w, A: r8 k! A" u: @3.listen监听,将接收到的客户端连接放入队列+ E# _7 l+ e1 I* r9 S5 j/ \
listen(sockfd,8) //第二个参数是队列长度
, {2 Z' f3 X6 k5 X9 O$ \* K$ c+ A) i& g/ K$ C" Z% \* J7 O, n+ M
7 x! [* f2 Z* K; R! E& {
4.调用accept函数,从队列获取请求,返回socket描 述符
& d$ T, u' @ G6 L. ? 如果无请求,将会阻塞,直到获得连接& N1 _& e2 H0 q+ Q- Z) [
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数% L M- O3 c0 p9 X3 r: H# k7 r6 L
/ N/ V! Q/ _4 Y& ^1 Q
* \4 ?; `' w: A7 Q1 ?3 C5.调用read/write进行双向通信9 ?" S H/ J* T# s. x
0 b/ R# A) f2 ~' B. Q% ^
! n$ p" ^6 y6 k- f7 G# ~; }6.关闭accept返回的socket
0 [) {* s3 A( Q3 M1 H2 \ close(scokfd);, b' a. l, d$ i4 g7 g
' g. p, `3 |: S7 m( H
# f; k% H. w# i0 j# J! o( ?1 ~2 j& Z( {8 ?% x
" K+ Y: n" o( k& c9 e
下面放出完整代码! t+ I& ?7 r/ z4 V2 P( c) ?- k
+ E, R4 N8 n1 H6 x o- /*服务器*/
) b& k5 R6 d: ^3 h# H$ I: T( {4 s - #include <stdio.h>6 F" t4 l/ z" h+ y
- #include <string.h>
2 X" L+ P% q; p9 K7 ? [ - #include <stdlib.h>
- z: T. E5 a" t" H% O - #include <strings.h>7 l- [; f: |: J
- #include <sys/types.h>
3 Z G) |& e6 [5 k - #include <sys/socket.h>( }5 h. q/ l" E' m) m
- #include <arpa/inet.h>
* l5 p7 u# z6 O, C3 F - #include <netinet/in.h>/ @# y' f3 x, E: n
- int main()
+ g- C+ u% A% n. E" K; O! K - {
n% f7 e% h( t! J. I* h! C h - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
7 C8 K6 R k4 P9 l$ n& q; P - if (sockfd < 0)/ N3 y S9 u. J6 L g4 I( R
- {% R9 w; ~ \- {) t# g3 U1 W& v
- perror("socket");
: Q& S; D6 _. V0 j. o7 ? - return -1;
( Y/ w& {2 Z5 c& }7 f( i! J1 z - } //创建失败的错误处理# e* w. @' s p! a6 k. A3 n
- printf("socket..............) h+ P3 z* Y) K
- "); //成功则打印“socket。。。。”( `) c: u. L, U' I8 q+ O
- + ?( U% B+ V' |" o" s
- struct sockaddr_in myaddr; //创建“我的地址”结构体; O" Q7 c4 h# c: O2 L8 ]% A
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
: V( h4 A/ n7 k3 v) u2 I) w - myaddr.sin_family = AF_INET; //选择IPV4地址类型( ^. Z! B4 |+ X. f
- myaddr.sin_port = htons(8888); //选择端口号* N! W5 Z+ k4 n. E. ]
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
! F# A J/ ~' K$ R; M6 a. R' e W - 5 b. P! z7 ?8 L* N6 W7 G2 z* p
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
- b R! j/ K7 m4 v( Z; F - {
[0 x, v) l, P/ E5 q; O - perror("bind");
/ }, x9 R E- N% F F+ O' z6 M - return -1;& i% l6 F( V: b9 i7 B
- }
' W! ~ b D+ Q - printf("bind..........9 A# U# I1 z, V, z
- ");. E$ Y% z+ N" J0 e) h* \* X0 {6 C
- 4 t4 |) F; V: ?3 Q; S$ o* r. a
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听. z& C7 V# _9 {2 @ _. T4 |
- {
5 B1 e# V5 S4 |4 F4 g+ O5 Y+ U - perror("listen");
! [/ a+ {+ o' A. b" o0 ~* P - return -1;
' q8 m6 P: F. [* x. A - }% f8 p0 m3 T x4 z: w8 {- C
- printf("listen............
7 D( D9 V! X& b; Y" I: K - ");% G% G5 h/ r( r( a
- , G$ C7 W- }$ W; ~; D( g
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
s. t+ t+ T, i9 ?: a - if (connfd < 0)5 e- D6 l9 q5 f( o
- {
1 H+ k$ _, l$ D* @' _. e - perror("accept");+ q* X2 t/ e- [7 o6 d
- return -1;& o- C/ f! w. P) `! s7 r
- } @- p, y* r* m3 j+ R
- printf("accept..............
- B/ u0 e+ ~" G# u# N - ");8 B. z3 n6 D& G: n' y
- char buf[100];//定义一个数组用来存储接收到的数据
4 G/ l7 K8 [# `% p) s( C - int ret;0 R, c7 b" H: h
- while (1)4 I T( w8 `7 L) n) i1 D
- {( v. [+ t" K8 W( |" `1 |" C% T
- memset(buf, 0, sizeof(buf));1 B) o/ k- [( W8 J) u
- ret = read(connfd, buf, sizeof(buf));7 r9 V, P8 `7 X/ W; _- ]
- if (0 > ret)6 ] @7 I+ r2 B \6 D4 y( {
- {. e' ]- |4 u. \6 _; m3 S, e
- perror("read");
2 C- `- v1 d1 j, V4 j* R0 J5 K - break;2 ?( x! o. V2 s& }; @; l
- }//执行while循环读取数据,当0 Z0 j3 j& i; j% v
- else if (0 == ret)
8 y3 X1 u. E- m0 P( E, |' ]& p - {
% t! Q( n4 Z# _ - printf("write close!
' X: s6 w2 ~$ \* n: Y - ");
- Q; d( ~% e. m6 y - break;- c, ]5 [: G: G! E% c
- }
- }" ~; G9 S, @- W, U - printf("recv: ");8 E- g! i0 p- c- `' F% D
- fputs(buf, stdout);//打印接收到的数据
* i$ Z) d" I' e$ F( o! a! B: G - }8 t7 N$ e; |/ I q
- close(sockfd);//关闭套接字
/ _2 a) j3 N6 m: b. u - close(connfd);//断开连接+ i, l" M5 z, V- Y, }
- return 0;. C1 c5 \- D3 N& n) I" w6 e
- }
复制代码
; @4 p7 v- J* i) o/ w% c! L8 G' U- R7 @0 y C
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
z9 \0 A6 g: ?8 @* } - #include <stdio.h>
4 K5 b' t6 z/ y2 j - #include <string.h>
' J* [/ z. Z; @1 a/ p - #include <stdlib.h>
# T" r$ I$ s9 U: J7 r" v - #include <strings.h>' Y/ o$ v. V" p. c1 c& c) H
- #include <sys/types.h>% O( b" s" Y1 L7 [. k
- #include <sys/socket.h>
4 c/ N2 s9 w, K. A* w - #include <netinet/in.h>* O7 Q9 S' W+ s- A. @0 L" i. c
- #include <arpa/inet.h>) ^; T- ^& C( J5 J4 L
- int main()
5 \' J8 |7 E8 N: Z - {
' f7 O$ a ?# y - int sockfd;. L1 T0 j, O8 L' N7 X
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
* C0 Z$ _" B. V! f4 ^ - {
; q5 j1 T0 E7 E. m: w2 Z/ H - perror("socket");( z& I9 q- q( X% B' n" R
- return -1;
" C6 [; @7 \) i6 X2 q6 c7 q - }
" R; a, t3 m5 l$ ] - printf("socket..........." y* N" T U9 V4 |
- ");* n" {- i4 M* N; T8 [
-
4 N9 s7 X# {6 l$ [( m* g0 B - struct sockaddr_in srv_addr;
$ D6 A$ w! ]$ G - memset(&srv_addr, 0, sizeof(srv_addr));8 u7 X% I# }- d
- srv_addr.sin_family = AF_INET;
) o( v8 j* k3 g. w0 H- V! | - srv_addr.sin_port = htons(8888);
- B" a, P/ ~ s( Y* r, E - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");( k: _$ k" v& G: k5 U4 E
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))# [$ X" ?0 ]1 N
- {# l7 x, e6 }: M7 P8 b# Z
- perror("connect");( Q; [# B& H* {. M) d
- return -1; //exit //pthread_exit8 K3 C/ j: U S
- }
* k8 h* s' Z& S - printf("connect............... `% T+ Q1 n9 k; k6 m9 Q3 |
- ");
; l3 C" \* Q& H% C' y% C - char buf[100];) [4 x6 A2 E# Y' R1 G9 v
- int ret;% u. P0 X; z# ]+ Y2 D0 p' j+ Z
- while (1)
' E/ o% E% Y5 C8 x - {. t' J" }0 N+ n
- printf("send: ");
6 w1 B/ r/ n0 J( R* c* e# W4 V4 r - fgets(buf, sizeof(buf), stdin);
5 ]& O9 K9 c! ?; B, y - ret = write(sockfd, buf, sizeof(buf));2 |" I k% s7 Q b7 V
- if (ret < 0)) x F8 l2 p9 B q0 M
- {
7 i8 V( _6 H- I2 z; n- I - perror("write");6 p" J) c' _! G- z. \
- break;& e+ `5 ]4 O# |: v
- }
- R' }% y5 a. ^+ J# F - if (strncmp(buf, "quit", 4) == 0)
[0 y) @ q, v: h8 e! e - break;
' [2 Z O) G2 z - }
( W& o0 ^0 f4 S( I. k7 ` - close(sockfd);2 x9 P2 q- i" `5 A4 _9 i
- return 0;9 a! Z9 g5 S- X
- }
复制代码 . f5 W- O$ i( N! w( p& ]
* S U* E: K* t9 U" {7 \ |
|