管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。; W4 K" ?+ N; E& V! B4 {
% O2 b# z, O3 m3 V# ~; x9 D7 p
. R+ d$ J& Z5 U5 b
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。6 i! R, |$ x4 X% b; o4 q) R Z2 W
4 \( n% m! O# a. R' K+ n7 K6 B$ Y- u- c* S$ f9 e& b5 i
TCP协议
8 V6 F1 f4 m" MTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。! K( }2 P4 C8 B
4 t% m) U/ d# e" {3 e
% E: \" o1 n. p/ ]1 u3 G, s. M+ ]关键词:三次握手,可靠,基于字节流。
1 y* M; h! q% E) p% D. i$ n3 s% F1 Y; g# a0 ^+ v# y% y) o
1 a. c! c& f! s# r$ W
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" M3 l( M% o/ z+ r* L" ?
0 N( ?3 ?9 ~+ E8 T- \" ?+ a' b3 kTCP服务器端和客户端的运行流程
" a4 R0 Z+ o1 x$ s
, L) ^' l& R; Y) S8 t+ o" A
K8 c7 U3 c( z, D4 x如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?' h% E R; e- h S. ?# Y
! x" L7 O9 f! ]. q% z! E& {/ }& E7 M+ j7 Y, W5 m) |3 o
1.创建socket2 F/ _9 Q; R! O5 F$ W
socket是一个结构体,被创建在内核中
8 N7 l0 ^6 L6 A/ ^5 x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议1 P9 F9 M9 z- [! d, {
$ _2 R( W' m( P2 b. |: x! A5 l) U% J [, I/ n5 C
2.调用bind函数2 Q& N. \5 g9 F
将socket和地址(包括ip、port)绑定。
" a( K: G# |$ B; F9 d 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
z' e+ Q( \3 y1 ~* r( M struct sockaddr_in myaddr; //地址结构体
. K: t3 a H8 [" Z$ @/ e bind函数
, G9 y* H, n3 X v( l5 M, c bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
, X3 {, A/ c d8 z7 |5 O3 ]: ~: [" r6 C/ l
' R2 R' J% b& w* n4 n9 l3 e3.listen监听,将接收到的客户端连接放入队列8 m1 ~- h* ^% Q7 V
listen(sockfd,8) //第二个参数是队列长度. ]7 r. j" J7 _- l7 k4 d
% {5 m; |' s0 z: w, B& _0 u" y
+ ` V( C2 x+ D4.调用accept函数,从队列获取请求,返回socket描 述符
8 Q* ~; ^% s y8 `! e/ u; L 如果无请求,将会阻塞,直到获得连接
3 E* }9 ~: X _- z" L; C0 { int fd=accept(sockfd, NULL,NULL);//这边采用默认参数9 Q6 i) J0 h+ x. S" }5 q
4 ?) a7 g9 ]" |1 m
* Q5 |% s5 C; A( [( h5.调用read/write进行双向通信9 f- \9 a; H# `' E, u
, D- B0 H6 U3 y* c# |; S L& z
! A B0 |: V$ ^* z/ l# ?3 Z3 {' t6.关闭accept返回的socket
' C9 D4 e; Z4 ^, M/ ~: U close(scokfd);
% ^5 v+ k( z9 R# h4 I. J X- ?+ c9 b* T1 T% Q' E6 {
. N2 {- v" J& ~ f6 D8 z" g: g [. Y, p0 {
1 l3 C. q1 M$ F) d
下面放出完整代码2 C7 v: t. S: t
1 }. I, T" y9 l3 \/ c
- /*服务器*/
6 ]" e" u* r0 I& y - #include <stdio.h>
5 S- a3 [; X5 h+ ? - #include <string.h>- G3 h; ~; C6 A
- #include <stdlib.h># @/ _3 N7 K" P- U
- #include <strings.h>
' e* `& _5 R5 V9 z$ T C - #include <sys/types.h>9 w' _3 @) C" a4 }& e
- #include <sys/socket.h>
; ^- ^9 z# v' ~7 [( e* B' \- K - #include <arpa/inet.h>3 w7 F$ [2 E$ I: ^$ c0 d
- #include <netinet/in.h>
- j% L* i0 c) [4 l/ O2 m9 z - int main()5 e" t* `! h, {, \
- {
?& q1 z. i1 E4 \ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- `- I7 J, I' l( c+ I g2 A( g+ }
- if (sockfd < 0)
( j4 M4 S( N" \' _: j+ q - {
" G+ W5 B. T2 i1 H$ b- u. U - perror("socket");! Y3 A. @4 d* a
- return -1;
& a1 z5 N& p" X! [( A - } //创建失败的错误处理8 d+ c, m! y, u9 \
- printf("socket..............) m( j; z3 w, [' N# w8 N
- "); //成功则打印“socket。。。。”; z- R) C- j: `
-
0 ?% x8 y* O5 o% V! o9 D - struct sockaddr_in myaddr; //创建“我的地址”结构体
9 X9 n+ A( q0 n/ L/ K/ p0 h% n - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
5 M! R7 ?/ `& f$ X8 r! S/ t - myaddr.sin_family = AF_INET; //选择IPV4地址类型
! m; n" D/ S: o4 M3 X' ? - myaddr.sin_port = htons(8888); //选择端口号1 M+ w& i0 r. Q
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址1 L4 ]" u3 C9 K8 V7 X1 O2 o4 Q
- ( H: s- v F0 ^5 a# |, M- u
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, n8 z. N5 c) l
- {- }7 p; O# B* Q3 R% O f' \' b% A1 n
- perror("bind");. E1 u# V6 D' i0 s/ k- J! W2 b
- return -1;/ s L, C3 D! Q5 [
- }. f# D0 N0 t8 O( N
- printf("bind..........1 b/ T( Y) f% H/ |2 }
- ");9 Z, l& I5 p" F5 A$ N
- ! k, G. p6 r4 K$ a6 a8 ?
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听7 L' U4 v2 {3 c2 P4 P6 g7 T: B
- {
& ~% X" H4 Y, n2 [9 m; k8 Y S - perror("listen");
4 L! j8 h) ~3 r3 i C - return -1;" Y6 r5 N1 t# ]1 ?) A; W
- }
1 Z' U5 k1 C" N+ h - printf("listen............/ z8 ?6 K) ~' [4 b
- ");7 c; G+ ?# x# ?
- ( k0 I6 B u/ S( G
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
( ^& M9 X2 @- r$ U, C$ D - if (connfd < 0)7 ] r5 X5 i* @- V
- {
3 ^9 S* ~7 k. t# `5 X* j8 ?" h - perror("accept");
8 P6 v3 h0 o2 Y" E; H' j - return -1;
& a4 T0 @* I; N9 ?5 |$ U u - }$ y1 E2 @. a0 D2 }, o3 M: U# U
- printf("accept..............8 V, ?# ^* n* ]8 y2 d& m
- ");! }. `2 Z* d% V6 m. y y
- char buf[100];//定义一个数组用来存储接收到的数据
9 G) U9 f! I0 |) M$ z0 w - int ret;8 p$ w. X) c& \ X l$ V" i: [
- while (1)
4 G. E+ X9 o4 ~" | - {
9 L. l2 J: ^$ f; L - memset(buf, 0, sizeof(buf));
+ M0 P% _, `; o ~ - ret = read(connfd, buf, sizeof(buf));
2 O- x- q3 l# o- Z4 V - if (0 > ret)
! r& C5 f% }6 A - {
( G# d6 X2 s" |* O& Z* o9 g8 P - perror("read");* O: g2 U3 L3 U$ [+ `4 p0 W# K* Y
- break;7 R" w# V2 t. R
- }//执行while循环读取数据,当6 ~, {# m* ^9 w2 A& x# K3 g$ {
- else if (0 == ret)" W& Q& f/ [% W; I# o! y; V
- {1 ]( c" ?% l z5 ]) R
- printf("write close!
) b# i* a: O4 d7 i H% B) u3 y. m - ");
8 S) ]4 |- w. k9 J - break;
5 N/ Y' A3 }; t$ P - }
) ]' W9 h* T% Z, I" }- J - printf("recv: ");. w3 }5 n) I$ e; H$ d9 s; Z
- fputs(buf, stdout);//打印接收到的数据
7 O! @' q- q" M1 ^" J& C0 T" P - }7 P0 c V: T) L( u) S Q* o
- close(sockfd);//关闭套接字) J) i* d7 [ b* `) a1 m
- close(connfd);//断开连接
; u: Q; g7 X9 B$ g* x - return 0;5 s9 S8 u/ R# f3 {. r
- }
复制代码
o- Z' b+ ] ?% c. F: P3 T7 f. g$ o+ k! L7 @
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
! [, r, e8 n0 L, N& u. D - #include <stdio.h>
7 ?$ J4 \' W7 Q5 h+ @4 y - #include <string.h>2 l7 C/ \0 Z0 Y* f( O2 }" L
- #include <stdlib.h>
: v8 F- U' L* G1 u8 p& n - #include <strings.h>( P$ V: k- d/ A7 [' }8 H3 G, Y
- #include <sys/types.h>6 c2 l( Z2 _1 w {6 Z
- #include <sys/socket.h>
& k* x, }* F; [) ?$ A: o - #include <netinet/in.h>8 L/ u0 Q9 V" ^: J" w
- #include <arpa/inet.h>4 T# c8 D% v* s8 E1 G, h. w8 S7 g
- int main()$ @6 S( C5 p7 |
- {
8 k8 L4 ]+ w) S' E - int sockfd;7 l! b: T$ g) v3 R
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))% @- C; o4 d5 O: D0 J: K
- {
|& }- Y7 \5 i0 z- Y& Y: u! s - perror("socket");& {# j! o' K& \4 c% h
- return -1;; H; b, d# @+ V- Z2 f
- }4 N, y' J3 ]. \/ B
- printf("socket...........
! n$ B; u, x! i3 N5 S+ Z - ");
( n; k- {: S% w3 W: Y" M - $ F" a; `8 ^+ }4 c% U: X" K* J
- struct sockaddr_in srv_addr;
' z: M2 [" I+ z* s3 g# u - memset(&srv_addr, 0, sizeof(srv_addr));
1 A; }! ~3 L7 y9 R. x - srv_addr.sin_family = AF_INET;; y( b# \) A& `+ u+ N! V
- srv_addr.sin_port = htons(8888);1 t" J5 Y6 `8 X
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");6 ^9 H) L( c3 u
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
9 r# T1 r% }' Z$ _ - { J# Q% x$ B6 y% m7 Q, e
- perror("connect");3 G8 l' F! C, q6 p
- return -1; //exit //pthread_exit1 Y3 `/ q9 [! b& m; w
- }' m9 t$ ?! \$ b1 V, p3 f* o# [1 R
- printf("connect..............
3 r: [3 \& g( o& [ - ");+ k3 D& T/ P" \% D5 v1 N* T# A
- char buf[100];& K/ y! O6 k1 j' l+ D% _( H5 ~6 ^
- int ret;
; c% w9 O% B/ r0 {- b3 w) T9 O - while (1)
! e' T6 |; x6 B# h - {
- J" T- P! x% e* E% H( s) Z9 i - printf("send: ");
: j- C$ v& C. b8 m' f - fgets(buf, sizeof(buf), stdin);
; h! P6 w( Z: H8 k - ret = write(sockfd, buf, sizeof(buf));) g0 }! K$ }1 S: p
- if (ret < 0) B2 {( Y) _/ @! n; k( B; C8 ^& R
- {4 a" G1 n; i0 T/ r
- perror("write");5 X; ^1 ?" l# f0 G! v
- break;
- S+ ?$ c F |; C9 J - }
- I: A& H4 E7 ? I8 o g, }! U! e - if (strncmp(buf, "quit", 4) == 0)" \1 Z9 y5 W9 {% L( ]3 r7 ]: B% B
- break;4 g* @* z7 \ q6 _- C! G
- }. Y, `: V3 I! e6 Q; l( M
- close(sockfd);; `: K7 a4 b) h% _0 W
- return 0;
* ^! Q) q* ]% g G) p( h - }
复制代码 & b; Z+ z) s! p u5 `7 t( I
1 L8 N- c% p! g) a1 v# w |
|