管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。; N4 Q+ z! i# G) E# U+ t
0 A8 \. m: T* C
% e/ @! H+ y5 t) c' x/ gsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
1 y/ `. j" v& G0 X4 J# y5 Y j* |% A7 h8 y/ @% F
3 b0 W" Z B1 Z
TCP协议7 D5 S: {+ [" A# a$ o6 y/ ]
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。" i. ?. b* D1 ^( {+ R3 K+ g3 ]
0 t: E# i8 s' u; }1 A" Z3 U
2 w! H5 J9 R6 g) |/ j; ~4 m* Q9 u
关键词:三次握手,可靠,基于字节流。
5 d, `/ w3 S7 L8 Z5 t
. r* [; d% ~# J1 [
. ~3 B9 M5 ^1 e5 }9 y; q5 G可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
% Q0 v5 H1 s! a; B: h
. h( h$ q, b' `4 @* C
TCP服务器端和客户端的运行流程0 f+ T) T% s+ N6 R& u
8 N; S# L9 z" {4 S8 B! p/ }$ `8 h) ?; H$ Z/ ^# b1 S. G% Z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?/ D$ A( V1 A) Z8 m. I
0 a7 N1 T* I( G
( `# w3 `$ [! Q6 J/ {# w1.创建socket
D1 i" R0 T$ ^ socket是一个结构体,被创建在内核中
% j9 E! @% s( _6 Z% k sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
- X; E! C* h' W5 Y& k( y% X
' Z! J, E3 K4 c r: a5 m/ x, C9 f( I% d' C& V9 l0 d
2.调用bind函数6 O4 t. }: b0 ^8 b3 S; p
将socket和地址(包括ip、port)绑定。 U2 z) ?! f# F: M: f. f- R" a1 x/ S
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
1 ^4 r, X9 a9 d) R2 X$ ~% Y struct sockaddr_in myaddr; //地址结构体
' x }& f8 `& d, b1 A bind函数' }& u3 L& a6 S- B5 v
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 y, m" o2 K& p& V2 s6 @9 d9 d% z8 A1 s7 ]' m
. b4 L! m/ I5 _ l4 \5 r* ]
3.listen监听,将接收到的客户端连接放入队列
( R6 P, c" w/ ^& g0 u5 B. _8 t) o listen(sockfd,8) //第二个参数是队列长度
' }0 U+ t# n. V8 b, {1 E9 H5 A( p1 n8 t
4 V! R! U. w' B G; c
4.调用accept函数,从队列获取请求,返回socket描 述符% T- O% r! }3 \ X
如果无请求,将会阻塞,直到获得连接/ b4 d3 F) {; _' H
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数$ Q& U- _* G$ |& m6 F0 S& @
3 o# o6 L/ K+ S/ t) x: j$ D
- S( {2 W2 F& x. m7 n5.调用read/write进行双向通信2 L0 n3 x4 T. t6 W6 d6 v
" F1 k5 q' Z- ~4 z6 D- [0 d
* f M/ n! N( b
6.关闭accept返回的socket6 O3 H$ T z% y) R
close(scokfd);
6 ]. U+ w4 }" [+ t2 T+ y5 V) q4 F1 T; B
. d! A8 p8 K1 k0 E- F" H" H7 g" j4 U
* n- R, }& T2 I1 a; e8 l8 s' ?
下面放出完整代码5 \8 |2 d: P$ [9 M
, e9 u b( A/ t
- /*服务器*/0 Z7 ^; K, ?) ~3 J6 w2 Y' _
- #include <stdio.h>4 L0 M: m7 M! s) y1 x) [
- #include <string.h>
8 ~, s5 W. N& |2 r( y( n' `( L8 Q - #include <stdlib.h>0 S3 O% r3 K" C
- #include <strings.h>) b) r. a8 f4 Y& E8 V2 P2 N
- #include <sys/types.h>
) }5 `' X' e4 _% p% P9 L1 q - #include <sys/socket.h>: Q" o: t# ?0 |/ r9 ?
- #include <arpa/inet.h>
% _% c5 B1 u9 K( }5 v& _ - #include <netinet/in.h>
8 m! z0 M- z8 a8 A0 h& ]: ]+ o - int main()
, p+ P C! V9 t! _) d! P' d5 a4 O - {
. q D3 b/ [. K9 E) d. B& O - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
/ B* L4 M* m8 w6 @; y - if (sockfd < 0)
* b3 y& k8 j+ Y2 d4 ] - {
0 `; b6 \. F% F" @1 K$ ~9 b/ C - perror("socket");
5 }- H! ]& r' U" x4 i - return -1;
2 o# {1 q- Z0 B: X! j) p/ X$ J - } //创建失败的错误处理 Q' N) z( c7 ^% J
- printf("socket..............
0 t; u+ z$ k1 O2 w( P/ p4 W+ `; n& \ - "); //成功则打印“socket。。。。”: ^- B% f" @" ^9 P
-
# q1 }1 U) x. B - struct sockaddr_in myaddr; //创建“我的地址”结构体$ ?: _ r" B# ^& Q
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
! y& a+ `( U- b8 R3 m - myaddr.sin_family = AF_INET; //选择IPV4地址类型+ ^- X3 @" x0 R
- myaddr.sin_port = htons(8888); //选择端口号+ I- @9 _0 t; X- S% v$ C$ G1 h
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
7 c3 R7 G& s5 e8 ~" O( {1 Y - 1 T: h. T9 S* ~" _ W
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
' }+ ^! J5 p v - {
: t( p/ [/ N& y+ l- F( b - perror("bind");
' G$ f# H7 q" ^$ J3 U - return -1;
- |: Z( [ @0 ]; ]& i7 T* U - }
3 e( F1 A3 l9 n - printf("bind..........7 K7 d; z! l" `7 D% @ Q
- ");7 L% u" r. t. Z# k& z/ ^ W
- 3 m8 v% U1 |- s; m. j
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
9 g) s% E$ F: _- R1 j, w - {$ j! x; g) o0 r5 r" }2 Q0 c' d; t
- perror("listen");
, Q3 q3 {# E3 V! t0 \' }3 ~ - return -1;
4 I: H& }7 Y8 S' L. T& d, Z - }. t& R* \ i6 R, l
- printf("listen............
% `$ S) z+ E g( p, A! f# | - ");
8 D; ^4 R& y9 O8 J$ R) s -
! {! z# o" [: w" E4 v6 O - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
X# j# i# w4 I: ]. l - if (connfd < 0)' C0 ?8 P, ~$ K$ }% N8 s# z
- {& E0 c8 e# ^$ }. G
- perror("accept");
]3 V0 s0 w: d3 I Y2 g# r* i2 t - return -1;$ A- q3 r6 o) E0 L6 ?' [# {+ j( z
- }3 {" R7 P3 \5 l" x8 S5 G) ?* d
- printf("accept..............
2 M4 }- L0 S5 U - ");
, b- i. q( u* E+ n: [: ?9 M+ r - char buf[100];//定义一个数组用来存储接收到的数据2 N% Q0 j' s: P% _8 P, [! r
- int ret;
8 p/ H4 |+ y$ X5 |. P/ E - while (1)& f5 @" a2 m9 p( J
- {# |" V `. T$ D9 C5 D- I
- memset(buf, 0, sizeof(buf));
/ f, K g& A& P {: b! P - ret = read(connfd, buf, sizeof(buf));
2 O* W( G: h/ n* g4 f; @0 h! k - if (0 > ret)
! u6 b; x6 u! H- D8 Z# T - {
& L: }8 z6 D9 m6 k/ X- a p4 s - perror("read");: ]& J9 b: P( b3 Q# N- f
- break;3 A) [- X* A0 k" V4 n
- }//执行while循环读取数据,当
# P$ D- g0 Z5 f, R" a# M - else if (0 == ret)
3 S% ~. }+ B+ A, f0 S - {
n. G6 ?3 J' p7 K. [ - printf("write close!
! ]/ ^2 r4 l% ~( _ - ");- x T6 k h% `+ q! ^
- break;+ ]7 Y/ o3 l+ i+ r) l& O0 @
- }
7 N: t/ t* c" Z D: S' f - printf("recv: ");
* y& p8 n' x* z! D' [' ~. L" O$ i - fputs(buf, stdout);//打印接收到的数据# |2 V/ y- i; a+ B# ?! k% k6 |
- }6 y( q$ K# `" r7 b6 g* b
- close(sockfd);//关闭套接字
5 Y6 C0 B" J/ d+ w; g9 ? - close(connfd);//断开连接- j: p5 O) }# P/ h) ?4 P
- return 0;) d! o7 l$ K0 e& D' t# V V
- }
复制代码 , u& O, W# o) H! q# w; M: k" {
$ y, ^6 S) W9 o6 b- J
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
8 D I4 x: F' ^' x - #include <stdio.h>0 Q8 A7 L# \7 f8 V: p
- #include <string.h>; y% r6 k% G; S+ x- F3 Y& e* U
- #include <stdlib.h>
. u5 K0 a3 E$ h, w+ ], k - #include <strings.h>
% R- | Y! s4 g7 M1 D - #include <sys/types.h>5 L; H6 C! l& ]5 u6 n+ E) G
- #include <sys/socket.h>
" F! x) \, m8 G( M, c: v+ N - #include <netinet/in.h>. w* o. _. m" M. e
- #include <arpa/inet.h>
% ]0 h, s! r! [1 r - int main()
$ v" e( b" d0 u! V, u# v - {1 y( k6 K' S6 z) Y v
- int sockfd;
c' j4 s$ A. s- L! |: o8 n* N0 d - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))/ H* P5 O. S: R. W/ G: X) l+ @
- {2 s! p' b( @; N. l# z
- perror("socket");
]5 T5 I: X: h4 N& g- U - return -1;- [' X7 P# [' H1 L
- }$ B3 ^1 k) T# G, M7 [* t6 y
- printf("socket...........- P# l" t' ?0 x6 O( b* v) W
- ");5 S6 z5 b, C3 f* S1 e
- ' o" I0 C( w* P
- struct sockaddr_in srv_addr;
% ~7 S# I1 {6 W* S' P) s, Y - memset(&srv_addr, 0, sizeof(srv_addr));) V) @/ i: I" t; l$ Z5 d/ A
- srv_addr.sin_family = AF_INET;1 I# O- k( j- |8 B
- srv_addr.sin_port = htons(8888);
0 o' i% m" Z+ q/ J - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
/ v9 x' f! _4 Q/ F+ I9 X - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
$ _% ~0 M) P; M2 ~2 V - {
. B7 K) c* d. I: I - perror("connect");3 V; z0 t, J2 F2 I0 E
- return -1; //exit //pthread_exit
, |& f: G0 w# c! U$ h3 O - }
# `; K: _: X( t3 G - printf("connect..............0 a1 k' s, a2 v- }8 I/ |7 g6 `& R
- ");- r/ N5 y' O0 W% W
- char buf[100];- T# Z; S9 l2 ?! D6 D2 }" }
- int ret;+ q5 O' s4 S2 e
- while (1)
" L8 t# w; j* {/ b9 j! y. k' W - {
# a# q$ X7 e: n - printf("send: ");2 f) S4 q4 D- z. d
- fgets(buf, sizeof(buf), stdin);
9 p3 M. ~/ ?, ?/ w0 y7 Z - ret = write(sockfd, buf, sizeof(buf));& B! K* V; j9 A- Z. Y# q x
- if (ret < 0)& D0 I, w5 H7 \7 D
- {# z9 T3 Z: g, @/ g4 F0 c$ G( F
- perror("write");% T1 W y2 @+ J0 z6 ^, d! O' g
- break;$ s7 m2 Q$ X) U
- }! M9 `$ U. D6 H5 ~* s3 y3 {
- if (strncmp(buf, "quit", 4) == 0). v% o5 A( s: k o& f: v
- break;
$ U9 X' z, ?7 p. E; g2 V3 H - }" V! I) I) L# Q8 q1 l5 j
- close(sockfd);" H- E+ H: ^4 A+ P W
- return 0;
, i2 I3 c$ M5 F E) R) S5 G - }
复制代码 + W, C5 ]& g: p( t- k$ l
, ^0 e7 z8 v% D) M# \# j% @$ {) v |
|