管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。0 x% k5 D6 {9 p: B7 ^7 U
/ N1 K. l- k4 _3 H, `
. z5 u9 a6 }4 |$ p/ Msocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
2 G( x2 m' m8 P
7 C8 i1 X" w3 P
- \+ ^ `0 E1 VTCP协议: A5 Y" [( |" s7 }# v
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。: O* A2 p. A2 G, P
( z# y1 r5 u+ b/ Z }
4 n) J- T2 e- n6 v7 Z6 e关键词:三次握手,可靠,基于字节流。' p5 y1 j3 _0 {
+ d3 E3 A, Q7 G9 o. I
3 X! J( ?3 l. |4 O& x' ?3 s, k
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 c. b* D. O& l* J2 Y! G
; w; j6 u1 t: r2 x3 T* |! B5 C
TCP服务器端和客户端的运行流程$ O5 h9 ^3 s1 e9 F
; Y- T, a! I6 ] [) M' G4 v: W% Q- A4 \) _( V/ m( B8 j
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?$ A" S# ?; A4 j+ W5 O: p
~7 P) n/ T) I5 x
6 X+ [) m: E! s$ j1.创建socket4 q: E' p- \. N% G; k
socket是一个结构体,被创建在内核中
3 W; e* t V [9 M! t" z- [: D sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ q4 D' b! m) J: _. E$ N
- G* O, X4 e# Y6 z- U$ |3 _! f& f% R* \6 P$ k2 M6 X: ^; H
2.调用bind函数
: c$ d" u! c7 K( p: i2 _! w7 b( m 将socket和地址(包括ip、port)绑定。
7 a; p9 [. L6 M0 v- U" i 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
+ O# y4 t m8 g8 `3 ~& T struct sockaddr_in myaddr; //地址结构体
3 _! [4 s/ J7 a2 A+ y1 ` bind函数
9 n6 [8 S9 c! \& W1 b9 S bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)) G" F" r/ I& Q- o' ?
# f% w7 l7 U- Y7 J7 u
" f, d/ Q' P l }% l N r3.listen监听,将接收到的客户端连接放入队列
8 d3 L' w0 O: E+ a3 O" T, C& o listen(sockfd,8) //第二个参数是队列长度
( [8 {6 E" t! X. B9 |! O0 ]* n* Q% }/ j' P
y" [4 g2 C; l, T4 X2 U4.调用accept函数,从队列获取请求,返回socket描 述符2 D5 [- t! l3 C6 y" s0 t
如果无请求,将会阻塞,直到获得连接/ N; l) p: O/ \3 n8 T
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数+ B8 U9 v, C5 i
7 x) V3 G @1 H6 d& I1 M! w
0 R5 T- h" r: A/ E" m0 F# r5.调用read/write进行双向通信
' V$ `7 l& {$ |0 Y& K
4 i6 i& ^! j5 P( e$ n4 T- ~/ ~' K, \
6.关闭accept返回的socket- y- F5 [# y, Z; J' T; Z+ B
close(scokfd);. y7 V+ v: i& M0 M: w) V3 o4 W3 f% I
" r% ]$ p8 z& n+ Y d5 [
+ g) h* O/ n3 a
. [0 [' S0 @" \0 ?& Y
0 O, h1 `$ W( S8 l& Y下面放出完整代码* ^. R0 J' ?, S- O# o
3 f. K% P) t8 q- /*服务器*/
; I/ d$ k" W1 t) A - #include <stdio.h>4 E5 O6 O6 W. t" B* }: w7 _
- #include <string.h>8 N6 z; J2 j' \0 t" W, k( M8 Y
- #include <stdlib.h>* R4 ]6 W6 n& p- _: s5 G, S: Y
- #include <strings.h>
- b4 z, y+ ~8 l2 `1 @& q) ~ - #include <sys/types.h>" Z6 w r" N: V8 j7 ^( I2 B
- #include <sys/socket.h>
2 a/ S, A" \5 s% V - #include <arpa/inet.h>
8 v1 o$ _5 w, K; d8 m% u) w7 I3 e" u: e - #include <netinet/in.h>
6 M2 i3 r1 P- |; j/ Y+ I - int main()3 i0 Y$ D1 i- Q( \
- {$ q( X+ n# ?9 T$ Y2 r
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字. c8 Q! n9 n6 ~
- if (sockfd < 0)
1 J |; H- h- N6 J0 Y - {! i8 E C4 K7 V+ _8 C+ w+ \
- perror("socket");+ R" R7 f! ~( o u0 u1 W" }& T# ~5 D
- return -1;
2 w D) R% v4 k* @* D% h: k, x - } //创建失败的错误处理& Y8 l' D6 D' d2 A+ ]
- printf("socket..............
" K1 }% M+ D& w0 M" S# R2 c0 i2 O - "); //成功则打印“socket。。。。”5 u# \& u3 n M
-
" ]9 |& m x! Z/ g! N! h) o - struct sockaddr_in myaddr; //创建“我的地址”结构体7 I0 E; y9 y1 u; {' {; [
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)# g2 _+ ?! n8 N) a' }9 L
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
6 H$ j$ ^0 ?- y - myaddr.sin_port = htons(8888); //选择端口号6 R/ Z& @5 A# ~- S p7 C" S" P6 @0 o
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
: ?+ O; O0 \( Y7 F -
$ X! B q# m& S* U5 u% {- V. ]& _4 a - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
3 L1 L: o# K" K9 z: J) A! g; C - {/ m* S, m5 ?6 x8 @" `) v" X: {
- perror("bind");* u7 c4 |) x9 r1 d1 t3 `* Y
- return -1;
5 g3 V- b% q" B- m" a/ ^6 J - }
: M) h: g7 W% }+ ^ - printf("bind..........
% M3 @3 p1 i+ ~" a+ J0 K - ");
9 ]5 j$ f$ U/ p% J: p+ r -
3 l# m X, t/ }. U, C - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- m; @7 r% Z1 F1 i0 u
- {, W! S9 O2 p) I4 ]* g# Q$ s+ X
- perror("listen");2 Q! u- g( ] b+ K' `. s
- return -1;2 {4 E1 D2 u7 O1 J3 H; g! ~% g2 Z
- }+ @* M3 q, h b
- printf("listen............
; o' d) `4 Z8 a. H0 q l) Q3 p - ");
2 Q) C3 Z3 l. b" R - + N* W+ C0 o2 \8 Z) Z3 Z* ^
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求% }9 t- L0 v5 n3 U% b4 x n. n
- if (connfd < 0)8 j. f7 _# N6 l
- {# _1 R$ D5 J9 r: L
- perror("accept");% Q' ^4 T+ V2 U0 k! E2 e# V
- return -1;3 L) n Z% m3 ^+ Y
- }
z2 o/ h, q/ L' U0 j* @7 j5 I3 \ - printf("accept..............* v+ L( Q5 Y5 B
- ");
' ~4 R6 i% _2 g - char buf[100];//定义一个数组用来存储接收到的数据
$ Y, t0 o$ q/ x6 G A3 H, E - int ret;* j2 ?, u) X% `* L3 {" x5 g* A
- while (1)1 H9 L, m4 Y6 l$ f' |7 x
- {
6 m, f% X- H2 c+ w* U: K - memset(buf, 0, sizeof(buf));
3 w1 t1 K" k0 d' f9 e3 Z - ret = read(connfd, buf, sizeof(buf));& j/ U2 G% ~: ^" {
- if (0 > ret)
% Q7 D2 }- P" i' V) F9 r+ O - {" F& h3 z( I6 K0 e+ p: E8 C: Y
- perror("read");
$ V. Z+ r# O2 q, E. c! x# P* F - break;2 Q h' Y$ t3 ]5 H m/ A3 V9 C
- }//执行while循环读取数据,当! X8 b& n7 q: B/ c
- else if (0 == ret). i! S' g; ~1 ~5 E; V. w
- {5 W' y; h7 R! M, [5 }
- printf("write close!6 c7 S7 E6 @) W, E* ^/ ^- j
- ");" e! I& r& {/ f( W) M! T
- break;! f q( s) F$ ]; p3 B3 X" v) ^9 `
- }
# U7 \. C% d+ X6 J5 ~0 T - printf("recv: ");$ C0 | I+ t( i J1 z9 a' ?
- fputs(buf, stdout);//打印接收到的数据
/ X. s8 _: B% J- y% B7 R4 i - }
( h2 B1 Y7 E4 B - close(sockfd);//关闭套接字
; k/ e! M* T4 z/ o' { - close(connfd);//断开连接$ f. U. V2 q) [# t
- return 0;
' F2 L& P6 F+ j, X& n' P - }
复制代码 & _% g4 x! ?$ ~, h
' M6 `% ]( j! W( L" ], w
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
G5 j/ a; f) x @$ N' i o - #include <stdio.h>
; m: t2 F; z6 s2 l, Z4 u) M - #include <string.h>
0 r7 O; C% @$ g - #include <stdlib.h>0 I# U0 F3 d. r8 R4 z
- #include <strings.h> ?+ E7 H0 `2 ^1 h( b3 ` P
- #include <sys/types.h>
. s& Z, h1 \1 k7 _% _2 q! w. [ - #include <sys/socket.h>
4 [) G* ]4 U. B5 H( x - #include <netinet/in.h>
, j, S4 W/ B% j) k+ S% M - #include <arpa/inet.h>
. F5 V$ E, ]) a# e$ j7 k - int main()
! K8 u- X* \- o. g9 N% X - {7 {5 F8 C L7 {9 A6 u( ^
- int sockfd;
7 b! d8 g; ^3 q. a+ g0 D& k - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))/ E9 I2 \( p+ j8 o
- {
& j( R5 p! P- \7 w T6 J - perror("socket");1 q4 I3 r! a- Y& P2 B
- return -1;* M# a# C- }. [. D6 `
- }7 z0 V7 Z! N4 \% u4 h5 z# R
- printf("socket...........
6 M* ~% r; F8 V - ");
; a7 @2 b+ d$ A: { -
B4 C! j6 `1 v4 _ - struct sockaddr_in srv_addr;9 ~" \. J* G/ M
- memset(&srv_addr, 0, sizeof(srv_addr));& W! y1 ]: j3 t; v# i
- srv_addr.sin_family = AF_INET;
1 f+ i2 T7 `3 Q - srv_addr.sin_port = htons(8888);+ x& l3 C9 _6 l4 {! X* H, Y
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
- R) Z/ J4 K* o - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))# `: ?2 v; a/ ?2 |9 |
- {9 X3 P: |7 ^- i+ O
- perror("connect");
" I4 D( w) \! J5 _; z8 K* j - return -1; //exit //pthread_exit& N, i. t6 w8 h' q0 ^8 V9 s, G
- }
. |2 m& P* g* r% l# i8 [& S- {" ~% V - printf("connect..............% d- J% R. x6 ^6 u& b
- ");
5 N O9 n) J7 \/ x7 d. F. w - char buf[100];2 v( K B8 }2 y, w* v
- int ret;
# Z& G* m1 k# y* d; V4 o6 ] - while (1)
& w$ g ~3 v# z9 H1 B" V - {) C' n, _, p* q& a1 w) [+ P
- printf("send: ");7 {0 I; P/ v5 u/ b: x
- fgets(buf, sizeof(buf), stdin);
2 O+ |0 k5 g' f) W7 C; g - ret = write(sockfd, buf, sizeof(buf));
# |2 l6 M" d5 P2 R* R5 W - if (ret < 0)
5 g9 U, l3 B6 ]% j; C - {
, e; B; i/ v4 O. y - perror("write");
. x1 e% h$ R% k3 M2 m - break;
1 C; _0 k1 b: h2 c- C- S - }
# t2 u4 f0 K, @( [. J" s - if (strncmp(buf, "quit", 4) == 0)
& J) F W3 g! O4 Q - break;6 U: `( T3 X T( v! D$ E/ K6 [# F
- }
9 R9 A" h! x: ~3 u - close(sockfd);
% N( u+ f6 f% @ - return 0;
4 f1 f, B6 n& g" L& _' T% ? - }
复制代码 : o3 g+ O' c2 }1 Q4 z# v% {
9 Y$ p5 S. j& i! l( m
|
|