管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。. t4 j7 k2 F( ~3 n- _
2 w* k1 \* X0 n! c `
7 e f/ T& N; M: h# s, ^" p
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。4 h+ J8 X2 @4 V2 ?7 j5 b& @
1 K& |# A+ W& q4 r* O3 F9 J
& D! j! ]/ H; d3 c! S2 m2 sTCP协议
; B5 j# l- E- z: E2 o$ gTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 w# l/ L5 A4 k1 s, g% C; g! l! U+ f: w
+ U" o- i V% v( ~$ n3 S5 v
关键词:三次握手,可靠,基于字节流。4 A5 E' q! {, h0 f& g) X
' R/ h3 m' G, J6 P- M5 x) ~4 k V/ b( n: j7 |4 C1 J( d
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。5 t: a7 ]& q$ R3 g2 h
$ B6 q0 c) T& d. \. oTCP服务器端和客户端的运行流程
3 P, B/ n4 _! V" }1 }
" n" {) b% k. E8 R) ^
; [. X1 v) e; f& I0 g. w0 R+ X如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 I9 B S. s/ m0 M
2 e: F" |% Z+ j# N5 G ?3 h* W; v' ^* Q' A
1.创建socket
5 E" w- B$ n; T3 X! f, q! d" z socket是一个结构体,被创建在内核中! B& I F2 ^# t, M* {
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& J1 ]" u0 q/ R9 u) l- y5 _# E$ J3 p6 `: f
8 v" y6 a# c, p* r* ^! z
2.调用bind函数% a) I& C% Z" v9 h
将socket和地址(包括ip、port)绑定。/ _3 s1 b3 o; g. J
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
7 x& b( }- O0 }% r' ~( |% [4 l$ n/ D3 o struct sockaddr_in myaddr; //地址结构体$ I+ V. x L4 ^+ D
bind函数
: K2 s9 q# U- q" L. Q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 S ~$ V) s" ~% y+ g
- s: C1 @5 a( {$ i2 e% A: g" @, U2 u8 X
3.listen监听,将接收到的客户端连接放入队列' _. }. c5 ^/ `/ E+ v: g
listen(sockfd,8) //第二个参数是队列长度8 r* K; H# {. ?" }6 D; v
+ C' z' I' z% p* o9 t6 L) u0 @
/ }+ m( v) N! v% q4 I# B
4.调用accept函数,从队列获取请求,返回socket描 述符
" O, L- V2 l, i* A: P 如果无请求,将会阻塞,直到获得连接
9 b+ T$ i3 d) f( l7 X int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 y8 y1 l8 }" z2 K3 I5 k# p1 O
+ K7 r |9 V3 A1 Q& g$ e1 S: }4 N" W* c& _' Q9 v! g
5.调用read/write进行双向通信" q K# c$ r4 C6 H' z
0 ?5 M7 p# Q* d$ {. d& C
8 [/ F5 f/ p+ r9 J
6.关闭accept返回的socket2 i& ]/ t* @9 R( j: {/ \# O3 T
close(scokfd);+ M8 o4 F4 [/ X5 W
) x* F: x# K+ _% u2 W' S0 g
4 j/ g3 F' V1 N* J
% Y2 Q/ p9 L, D- ]! v$ x8 ^8 [0 C, h+ ]5 Y
下面放出完整代码
4 l3 O# y' p' b9 C9 ]; Y# b) c( k6 G% R. F+ k
- /*服务器*/
- z+ f# M) c6 m' X4 F" B - #include <stdio.h>; S3 S& K& r5 W1 @
- #include <string.h>% i% B% j' n3 e6 r ^7 L# E
- #include <stdlib.h> B* R+ i8 P% u- o
- #include <strings.h>
* ~% Q. ?" [& D3 ? - #include <sys/types.h>
/ L! x x% v5 _3 q' ]* I2 b" n - #include <sys/socket.h>
/ v0 A, H+ J" v. S6 }1 _/ a N - #include <arpa/inet.h>% n( \0 o, ?; k' w: ?0 t# F. j& p
- #include <netinet/in.h>" ?2 z+ |- @! e! `
- int main()
1 p3 L0 j+ F, v( w) N" s - {. H/ f2 Z4 M1 U: V! U' R2 B
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字% M; V7 i8 ^. [' b5 n
- if (sockfd < 0)" H1 C8 t4 g6 N$ j1 a0 s
- {
: c1 |) ?# g& }1 S7 K q - perror("socket");
9 v% D" ?; l/ X c- G) s" { - return -1;& N5 s9 n* p* C5 |' M
- } //创建失败的错误处理% O1 }% }! n D3 G2 _- W- [' V* t
- printf("socket..............: ?! Z& C: g: l G: X
- "); //成功则打印“socket。。。。”( o: C( `2 D* M% o
- - w7 \2 {1 }' v' ]
- struct sockaddr_in myaddr; //创建“我的地址”结构体* B+ z' W: H7 M- E
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)6 u. F5 j3 E+ i2 f- n
- myaddr.sin_family = AF_INET; //选择IPV4地址类型9 j6 O1 G! \: q* E! _
- myaddr.sin_port = htons(8888); //选择端口号+ O* z3 q! I/ Z* g, P% `
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址5 E' w3 W+ c. ?0 |$ y
- ( {* w/ l8 O+ b: z2 v& s
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
+ [ A7 w, I5 H% _6 z9 \0 l& I - {& L- r$ a. E& `$ h
- perror("bind");
+ \9 n8 H* C7 W. y; `$ y1 N& S - return -1;
% P# z+ v2 _, o J% E! K$ | - }
2 v( @: j3 B" ^ - printf("bind..........3 m' B0 }: I! l6 }# B5 D
- ");
6 ?% |' i$ l/ ? - 5 v% A" k# i" d( v5 j0 T1 h) J
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听* [; f/ H+ {7 _: P; o
- {* G" n% a) ]% J/ i( y) {( a
- perror("listen");3 D: c) E5 H( a
- return -1;/ e4 u5 l! D" @& m% W
- } L6 q, O t) L# _# `/ n; w/ C
- printf("listen............
- Q0 h( D1 M8 N) C) x' M0 ` - ");( C, g" o+ |. }7 o
-
- F ?: A T2 I0 {; f$ X - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求- E/ _7 l8 b7 A
- if (connfd < 0)
- c2 U v; r/ O* G7 ^1 c t - {
2 y5 Z3 y1 j$ U+ H% @ - perror("accept");
( H4 L5 R8 D" H( v - return -1;
; N! T q7 Q. s6 g }/ S O - }
, ]( Z! ?; l2 n4 p- _# L9 h - printf("accept..............
0 T$ B! a! T1 A& R - ");
1 `- }0 u$ `/ ~7 E: {6 I' ` - char buf[100];//定义一个数组用来存储接收到的数据
j8 z* T7 D, o - int ret;
4 p4 M$ e: v+ h {7 S2 l: ?% @ - while (1)4 S: y3 l& |9 m( a1 b1 x$ d ^
- {
% r2 t0 F- c" V8 E5 |+ N - memset(buf, 0, sizeof(buf));- w \$ m8 C: t, v I
- ret = read(connfd, buf, sizeof(buf));. U+ A m4 a, ~$ C, Q/ O+ d
- if (0 > ret)- i; K( Q) Y% a/ A X8 b
- {* g! C) W% T5 Q
- perror("read");9 W e7 l- R1 [8 ]- K
- break;. F( V5 ]3 D: u: c$ u
- }//执行while循环读取数据,当. Q. P. L/ j, P' J
- else if (0 == ret)
j5 N9 a2 R2 K; O$ B - { c) _" e" s' |0 S' i: O
- printf("write close!
' ~2 W2 B8 @) f - ");% n7 l' k; w$ Q, g
- break;) L9 a: E: M. ?9 {9 v) L
- }% w) d* v p5 D. l0 s
- printf("recv: ");3 ]& R* }( W" Y; I, j: B
- fputs(buf, stdout);//打印接收到的数据
- h. C% N, M; i/ ~& ~ - }
' U# {/ ?: B5 V+ R; F - close(sockfd);//关闭套接字+ J. w1 O3 K1 r1 Y" s3 \6 B! R
- close(connfd);//断开连接! b" H1 U3 t: X& S
- return 0;
7 |' c [# i) y+ o/ E7 K( m - }
复制代码 X% s/ S7 P: T, H' x
6 Z* Y& d) X6 {, |8 ~) x
- /*客户端*/(具体功能和服务器一样,所以不再加注释)0 C. l" a' o0 \
- #include <stdio.h>
& u; p K2 V/ R8 C - #include <string.h>
, F( _% T% Q0 g l+ C - #include <stdlib.h>2 M" [% {. _# u3 W9 W! N4 }# L* ]; T
- #include <strings.h>* p6 T& b u9 z4 j i
- #include <sys/types.h>- V+ L; q: B4 i" P
- #include <sys/socket.h>
: D1 c$ k3 |1 ^1 x - #include <netinet/in.h>
" f6 e) h# `; J7 u* `0 {2 Z - #include <arpa/inet.h>
( A; x) D) A) `: v - int main()6 r; D* T' Z7 C8 F7 A/ D9 G
- {8 y' u @- f7 Y! o5 l/ j; F7 ?8 ]( ~
- int sockfd;
5 d N( {0 d' a8 t9 v, i H( [ - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))+ x! I) J8 k( E
- {
! V1 l6 z2 W1 p8 _ - perror("socket");3 }. c) y; p% B2 [2 E, d
- return -1;
7 d# I7 n+ K. M, Q" W - }$ x5 P- |, Y6 g8 L/ _, w
- printf("socket...........
0 s; |8 {- _8 Y j+ e - ");7 E, g) V5 S1 { H& Z2 I
- $ o; e7 H' |" T
- struct sockaddr_in srv_addr;% t4 y! h; M( b6 e' D% S
- memset(&srv_addr, 0, sizeof(srv_addr));
2 j* E. t: B7 f1 _ - srv_addr.sin_family = AF_INET;; Y @4 T5 A! R+ T, g
- srv_addr.sin_port = htons(8888);
, g5 v0 d( e6 ]- E. D% V4 ^ - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
! q! V/ {6 K9 @* g - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
0 N( p" i U, T" i - {
6 [! D: U6 \6 U; m! L - perror("connect");6 H- Z# i$ M5 \$ J0 j1 G9 [
- return -1; //exit //pthread_exit7 v+ B" e9 Q j, _+ D
- }2 R E3 ^0 a, c! {
- printf("connect............... o8 W9 [- t' R2 I
- ");
) Q, n" i8 L+ f. F# ` - char buf[100];7 _% _. e9 _, \ w
- int ret;* Y/ ^) d9 z4 F, B; u( y
- while (1)
- D# i- O9 l3 }3 } - {5 G) R& e- {9 g) k6 q! z2 O; [
- printf("send: ");- T5 f1 g, U W: z8 g* H
- fgets(buf, sizeof(buf), stdin);
, u8 n& J" A7 G/ |; `# I, | - ret = write(sockfd, buf, sizeof(buf));
: Z) }- e/ e/ l" ~" p - if (ret < 0)
+ g* b, [1 K: P - {
* F* q3 j) o7 k+ Z - perror("write");4 \& Z. C u- v" l- B, ]) m
- break;
" r5 q: d. w4 e - }
* D2 {: K5 r9 H7 ^1 i - if (strncmp(buf, "quit", 4) == 0)$ c% E8 r$ U$ z7 H
- break;
6 h. A4 k" a+ R0 W, f8 x4 o2 c+ |# w - }5 Z1 a! F% x" M+ W7 l- U- ?
- close(sockfd);0 w w9 d9 X9 f
- return 0;
( b7 }) e6 z: r- c- E - }
复制代码 ! G" h6 r# n1 ]( l" i3 E) X! d( a
$ h+ J/ |" M' e N
|
|