管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
5 b5 n& f7 o2 T/ s, k- o7 I
% d% P/ V, I! K6 y i+ k) h" d0 h: B% ?5 l
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
6 K# J; N8 N# Z. N
7 ?; e; ]2 j. ]4 L& N0 I+ [ ~+ x- c: V1 k
TCP协议
; n$ O+ v0 \- C( oTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
. x! m3 V0 f0 R* _- d% Z% |9 j- M& l, h* w9 R
* c- @/ u! C" {关键词:三次握手,可靠,基于字节流。 X& W+ k" g- j! G- z
4 X0 u$ `) q" [, g5 C0 K$ f4 E. @9 o* N
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; d, A3 \ |" r8 n. `
! ~, Z S7 G0 v- F8 Q
TCP服务器端和客户端的运行流程
1 ~+ w) F& g1 k3 S2 [5 Y7 l% W2 x' O% H, u- |4 B( Q4 v
1 \! M% ~9 e$ q0 X v
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
- r. u' [( t+ r$ r9 y7 ?" b$ b& d1 y, u7 W3 H
( K, H& A! B# y3 e+ e" x* O
1.创建socket
5 ~4 a) ^+ j4 b+ v) p socket是一个结构体,被创建在内核中
9 x( P6 k) Z0 N# ] sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议7 D$ d0 f% B% T% m3 Q8 t2 R! M
8 K2 c: e3 E8 L e
4 n) L5 E3 u3 `. B, T* M8 ]! T& A, ^
2.调用bind函数. D1 ?: ]$ B4 ]
将socket和地址(包括ip、port)绑定。5 L) V' f! ]" b
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
J$ D- a2 j2 D! s$ Z2 W2 W8 E struct sockaddr_in myaddr; //地址结构体8 ~: D0 \' x$ D: o5 \7 _- b
bind函数
6 S6 V2 h$ n$ q& N$ X bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)), ?8 A: k: t8 I, e$ B
/ Y5 h5 I$ y3 m, X6 J
: W* T8 U9 A7 {% A3.listen监听,将接收到的客户端连接放入队列3 Q2 C/ {. d7 \2 J7 {6 v% X
listen(sockfd,8) //第二个参数是队列长度
% X! W' ^ p( Q. g* \& ~; i* f# |# v; s
3 o6 R3 }5 R+ \# u4.调用accept函数,从队列获取请求,返回socket描 述符8 \6 d( J1 T4 m# `
如果无请求,将会阻塞,直到获得连接4 c$ Z( u% }1 p1 a7 F
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数) n: w( p# [$ Y2 p/ F1 c, |, ^0 e
K& H8 i/ F+ X8 p& Q
, |0 E& l. A1 J8 {- m. m; V# W5.调用read/write进行双向通信
3 p# g6 P6 V j1 f" [" ?2 v# o- o3 c3 c- H( o4 E2 M- P& B. B& Z
6 W$ S" X* T. O
6.关闭accept返回的socket
! K$ c# s0 L. O L& ^ close(scokfd);
9 ?% P. D' y* V: a
& Z% B- p( `% i( @
' c4 O- W+ r3 J' ^5 p V9 ]5 p6 L3 u/ _+ W C- n. f
& [- W% _9 ?6 l4 l4 m! N3 v下面放出完整代码$ n ]. o2 z1 }3 C1 i
9 ]* |" ^ e# g) O% ~- Z2 F* n- /*服务器*/' Y6 L, H0 Q: Z9 J
- #include <stdio.h>
/ o {. l9 p9 l2 X6 h+ W6 C D& a - #include <string.h>
% T8 }4 M! [5 D4 } - #include <stdlib.h> F R9 W. m2 l/ L" n/ N$ L G% j
- #include <strings.h>/ o1 l" t/ \4 r0 Z1 \
- #include <sys/types.h>6 F& a( W4 `" ^
- #include <sys/socket.h>
) \6 ]" R! W* a* l+ m( j% K - #include <arpa/inet.h>( N( L ?7 J; R; }
- #include <netinet/in.h>
5 o" ?/ @6 q! q+ q! U W. P* v7 T - int main()0 O. E& O7 d( ^2 f
- {9 F4 v' n2 A$ C3 i. A% A
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字& w1 @3 @# J4 N: w/ A. g
- if (sockfd < 0)
& `5 Y! O- q* \' x" a - {
8 X" p+ z2 ]" G& _# d4 S! N - perror("socket");+ k3 g& `3 `* ^8 l7 p
- return -1;
! D' _1 R# V6 K L5 D: o - } //创建失败的错误处理
1 M9 S0 |, M, e - printf("socket............../ ~- i; V7 B0 v/ u" }
- "); //成功则打印“socket。。。。”
\/ O" i/ B$ i - & f3 u7 a; p# z, X, `0 D
- struct sockaddr_in myaddr; //创建“我的地址”结构体( D/ [1 n( i4 i% K
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): M6 T; J; [/ b0 D0 W; @3 r9 c
- myaddr.sin_family = AF_INET; //选择IPV4地址类型5 }2 w" U" l8 M! W9 W
- myaddr.sin_port = htons(8888); //选择端口号
( w+ ?0 T( N% ]' W- Y - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
% y/ U# L) c9 p+ J -
. k. v0 w- h- F* h( M3 i - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
" D/ e. e3 D6 T0 E9 G8 M9 {7 g7 m - {
, [6 B9 o% ]: _! v - perror("bind");
% z* ?% z9 l9 x3 \# |% X: _5 p, T& T - return -1;3 ~ T M1 _" C( W4 _0 ~# Z6 `
- }, m& j2 ~+ E8 E4 D. L' n! Z
- printf("bind........../ b4 x+ Y1 |* p6 R) a7 q2 `' `
- ");- S d* E7 C! _, |& u+ E& ^" }
- : }0 E' ^2 x: U/ `6 w. W* @8 q. m
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- b. M. C) s4 Z
- {
# N# x: @/ C) f* a - perror("listen");
) } E7 v$ L- {- M' c J - return -1;' a i& @9 q( d( d$ {& @
- }
, w0 l; z+ ^. g& b) U/ m T- Q" A - printf("listen............
' f; u& M' b7 d0 t8 W4 L - ");
, y% F; F. O7 k5 J3 w5 ~* ~ - 7 ?7 b1 H9 H! _4 V( {4 k# o
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
( c5 |0 \2 Q4 W3 j0 R0 H: @ - if (connfd < 0)9 Z2 |# y# c5 ?' `
- {% X" j" `0 | j2 `! x$ {3 f
- perror("accept");
+ n2 P4 o L4 q5 F, I8 h - return -1;7 o3 V% m( D3 K% a4 M% o
- }
" t' l' W+ M9 ~. I2 X4 r - printf("accept..............
4 `9 Y1 _8 X4 j0 `+ ]# s' { - ");4 c* c% d4 G1 M6 m% C" K, i6 W
- char buf[100];//定义一个数组用来存储接收到的数据& l3 Y# F' c9 o4 m* V
- int ret;
2 `/ F# Y) P$ R9 a) v' t2 x* Q - while (1)
: ?1 B7 H* v |2 h, J3 X9 q - {, j. F* U( f8 A! j; A8 t: ^& M
- memset(buf, 0, sizeof(buf));
8 c) R% p A$ j) e - ret = read(connfd, buf, sizeof(buf));
7 N0 J$ c6 f& |9 s' u( k3 S$ o- M! K - if (0 > ret)! I+ P7 C, y$ n# e4 Q' T
- {
8 u K4 |' Y; j) G5 Y - perror("read");
& e9 ]3 g4 ]8 Z% s+ a! |, D - break; W/ V4 u) p, ]0 X8 e
- }//执行while循环读取数据,当2 ~3 \4 @3 C+ s* t5 {8 f; o) \# a
- else if (0 == ret)* w5 a1 z- }+ |6 B6 R5 H; T4 W
- {" J4 t7 P6 D R9 f8 a
- printf("write close!
$ e2 X$ C. l1 [ H - ");( n; f. w7 U2 c) j+ u1 \
- break;9 _7 N: F F$ o
- }% u0 F' C9 X% ~ S
- printf("recv: ");
4 i6 y% R. u! \# @ - fputs(buf, stdout);//打印接收到的数据
3 W! G# D8 ?5 h- q - }
6 h# O+ ?/ u1 S* J+ g1 z! A7 u - close(sockfd);//关闭套接字% h2 m9 v7 L% u& _$ e6 j
- close(connfd);//断开连接
% l% r( Q8 k9 Z" \ - return 0;: t8 m& b; U J4 `3 k$ n& N0 p
- }
复制代码 ' ]- S9 u: `$ G
* M4 I6 ?$ H1 b7 i5 R- }2 l/ U- /*客户端*/(具体功能和服务器一样,所以不再加注释). _; I8 e& }0 ?" L1 C
- #include <stdio.h>3 A; {9 B) `2 V+ w
- #include <string.h>! h0 ~5 W) e8 l3 _
- #include <stdlib.h>2 X5 W6 r! j9 U0 E2 i5 @
- #include <strings.h>& ?5 H3 k/ `9 `6 ~4 L1 A
- #include <sys/types.h>
3 h( ^& t4 F+ R1 v - #include <sys/socket.h>
3 I4 P u2 a5 E - #include <netinet/in.h>
" r7 D. ]" k, c7 y' I, c5 a - #include <arpa/inet.h>
3 U5 E+ }2 s: g5 V) \ - int main()& k6 @# e& n B
- {: A h+ ?9 X1 P8 ?% G/ `$ j" w# C# u
- int sockfd;
& \' ?: {( n( X8 |: u - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
6 { W9 O4 z5 ]) b - {3 f' x0 l; i: x$ D8 m7 }
- perror("socket"); q7 H2 F; G9 V) R
- return -1;9 \! d, d0 A) U; q" L7 {
- }; Z* J$ V8 [/ }( X0 j$ ]
- printf("socket...........! k- }/ Q, x+ q% i" Z
- ");' Y1 Q! b+ r5 R( M* X( b& P
- # r) @2 t: O' S+ e
- struct sockaddr_in srv_addr;
# H" A$ ^( X. Z. n1 n! O - memset(&srv_addr, 0, sizeof(srv_addr));
- P& M0 y; l' K" Q$ `7 c; ? - srv_addr.sin_family = AF_INET;
1 v3 L5 D# W& S3 Y - srv_addr.sin_port = htons(8888);2 H' w$ m( Y* D- |
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169"); F: E7 D [8 _5 K9 F9 o& T
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))) [ V+ M7 n9 \! X3 R" z+ i
- {2 y$ Q1 g; E j
- perror("connect");
, }9 |( j( |# y& y' t+ z - return -1; //exit //pthread_exit9 |# J2 V- y, {
- }
1 [% D# x7 h) T$ t# Y- Y* B - printf("connect..............) _* L/ q5 m+ c$ d
- ");
: T, b2 w d/ S& j7 v: a( ^, }- M - char buf[100];
+ k, d. L' Q7 M& h& x S! [ - int ret;
6 Y- @8 _" w9 {7 q" j, a# K6 w! U - while (1)3 o! }& H( [& J2 t
- {" O- d9 Y: H" Y. ]$ h |
- printf("send: ");
* N9 ?( ?* ]8 R) t/ } - fgets(buf, sizeof(buf), stdin);
0 q9 v; c' O6 S& |( t( D8 l - ret = write(sockfd, buf, sizeof(buf));1 g9 z4 H: ]0 j) r w
- if (ret < 0)( l) D, `8 i. z5 G8 t# R
- {
* A0 W# q0 j: h4 v, C9 X+ D( k2 ]0 o - perror("write");6 s2 R$ E7 C, }( d" I
- break;% X) Q' f) z$ V% G' Q* w( H% ^" |
- }: n4 ~5 n1 \2 ~
- if (strncmp(buf, "quit", 4) == 0)! |4 P' O7 T2 l4 p5 r$ O
- break;! H" v8 |1 e: u9 m7 K
- }
: V1 y: u8 e8 a# L5 | - close(sockfd);
; a; k9 A9 g- a - return 0;
_6 V5 Q' k5 V- \ - }
复制代码 ; s/ T, x7 R0 Y3 [
6 i) L- X' q; H# |8 D+ {9 G |
|