管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
% d3 `& N6 T( `$ i' t
' L4 ?3 Y" \ V' V( v2 s
! K( d3 ~8 ?" X' ysocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
4 T- S3 r8 X% Q0 t4 ^4 @( m9 N- G" V: b2 n/ g) G
, C2 v8 J! a8 N- _6 {TCP协议6 X0 N" Q3 M! [" m9 C$ I& b
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
9 s! e4 e4 D' m' d& ?& o) \+ W$ y
; h) I7 r7 f% l4 H& o$ X/ p
0 C4 O5 V. C- w8 j7 p关键词:三次握手,可靠,基于字节流。. m. X$ h5 D- r5 Z; Z/ n' E
8 @! M: f! d9 D' d/ G: R1 n; @
7 a j+ Y& W. r9 r% I$ O- j可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
! I+ a) c& P( T E! K1 ~1 H6 h& X
/ T) h; C/ f1 j$ D, xTCP服务器端和客户端的运行流程$ ]7 g. `, k5 ]0 m4 \: \
5 J- v3 q' W: y( O- g
! |/ M" @8 T X8 z, H9 A
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?+ W3 N7 \1 g3 v3 N( P6 N
& ?! v% w$ Q; `# P
' v; B1 E6 j* ^; {3 J1.创建socket
" S; }6 F# E: B" J; `8 w5 b' j socket是一个结构体,被创建在内核中
4 m: ]& K a7 n6 k' y. L# O sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2 C7 D" f9 Y6 O
' M6 Y+ T( B3 t* g9 B0 `8 p7 \. L# q" l, ~2 f; E) M- R# l
2.调用bind函数
/ W1 ^7 C9 j% Y* a 将socket和地址(包括ip、port)绑定。
; s/ P9 s2 I" @4 K 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序! I* d2 p" v: n* {8 S: [
struct sockaddr_in myaddr; //地址结构体& R, A; t1 i9 @
bind函数
. v# F7 K( k( ]: T$ t; Q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
" I( T2 d/ b6 W! Y* @' \, O0 Q, ]/ H3 K* m$ `
( K9 d: [; x! D# [3.listen监听,将接收到的客户端连接放入队列' }/ [) O$ K% T+ L O* W3 B# z
listen(sockfd,8) //第二个参数是队列长度7 D) |4 t& |% _; i& m8 R
* Z( y A( I8 ]* e& L# q( }# r, c
4 E: P# H2 a% V2 @4.调用accept函数,从队列获取请求,返回socket描 述符/ s& V% G5 X% t: w
如果无请求,将会阻塞,直到获得连接5 G2 Z) m g6 y; t( s4 [
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数# S- I' e3 V- J
& {. f; x0 ]- @( l0 {/ v# ]- X
& y6 x* V$ g) h
5.调用read/write进行双向通信
* ^2 Z9 @' t6 a ?: ^+ w$ t: v1 N5 [+ [2 w) y2 R
. e( }: {$ x) e) `
6.关闭accept返回的socket; e, _8 c8 p1 ]- O
close(scokfd);8 t) N& G! ]: Z* P& {* q
4 l9 ]0 S @# G9 y: ?9 _0 P$ i5 x' h; o2 [
# F: V1 R) g3 p; O- K
: ]; H( W3 ^# u+ g- h下面放出完整代码# c& {3 x0 V/ ^! q, _
, Y. I$ P! p( ~. X
- /*服务器*/+ l k2 x! V# G; U) o V+ M
- #include <stdio.h>
4 N5 S3 M7 A/ m8 ]4 a" N* d - #include <string.h>1 W7 ?& i3 X% ? p; I3 T. H! ]
- #include <stdlib.h>
/ x5 g, G8 O: {6 b - #include <strings.h>
, \- b1 e* g8 J - #include <sys/types.h>
, O$ E$ l% D% g: v# X: S8 | - #include <sys/socket.h>: l4 i, n- K ?% s* P3 r
- #include <arpa/inet.h>2 A- H I+ ?" ?; K, F$ `2 a
- #include <netinet/in.h>4 l! w B; ^+ B5 k3 e- H2 U7 R
- int main()- `* t L, P2 a1 {. M! T) P- N$ _
- {
5 o7 ]* M- V8 W - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字( l4 }( t7 A: x+ t6 R( F J4 q' j
- if (sockfd < 0)
9 U3 r o/ x0 f7 e! t, A% a - {4 m. ^0 c1 f! |! t
- perror("socket");; V: W2 d9 U3 P: B4 C8 [- F5 O
- return -1;
5 s$ l) h, F$ l - } //创建失败的错误处理
/ n( P8 c: P% k# [3 y - printf("socket..............
, Y+ l1 } z9 E+ o9 ~( E9 `' J - "); //成功则打印“socket。。。。” L* m, }1 _. J g! O$ S% n1 B; k
-
3 ~- e/ a. k- j" S7 Y - struct sockaddr_in myaddr; //创建“我的地址”结构体
: ~. j5 g! l' D# w! d5 @4 t - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
$ t! i( y: X J8 T7 a8 G - myaddr.sin_family = AF_INET; //选择IPV4地址类型
# N/ _/ w; a4 n5 G- @- A } - myaddr.sin_port = htons(8888); //选择端口号" H9 p1 M$ p+ X" ]: _7 X
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址+ K1 V# D- \, o9 k
- - E" R h3 E# a
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字 [ P: s0 p. @4 t
- {
1 x* Q- R7 L2 ^ - perror("bind");& [0 T9 i, C, F' G }) O! C& y$ a Y
- return -1; h" R5 C) S# M3 E: g( T
- }3 v; n- f& A* ^$ F- t
- printf("bind..........( ]5 U& Q9 G( N
- ");
3 z, a3 ?5 {& C7 y. e* R -
1 K$ J, m) m) P& x0 O - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听! R& L; `5 O# `! {9 e( c$ t
- { ?0 X5 G8 Y$ P0 m4 p
- perror("listen");
4 {( g0 |4 {3 l! I - return -1;, i ]' g! o9 _7 g: u B# r
- }; L% l, ?# x* C6 B. y- `1 P
- printf("listen............
2 H. h6 H4 |# w$ u- j; l" _$ U - ");
3 G% M( g* V B - + G) Z3 _5 d+ J3 B* D- Q
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求+ e( n) a3 j6 L4 B3 S8 p& l
- if (connfd < 0)
6 b! j# w' n! V0 {( u7 N - {
- U. X3 H3 z, @( b1 Y, U! E - perror("accept");; w6 X P* Y9 q$ C- Z4 T3 P
- return -1;% R3 p i% c* P8 x* E& n
- }3 z2 ]4 a3 n. t5 \
- printf("accept..............; l8 Y6 [4 g+ n0 i& r) Y
- ");
) d, ~ J" b, x7 U& t0 }+ i( T - char buf[100];//定义一个数组用来存储接收到的数据
Y; W* A2 r2 D- \9 I - int ret;( M, `7 _1 [$ j" M! o% X
- while (1); K9 z8 p7 F$ Z0 Y
- {
7 v5 u1 f8 A- y" B3 U! h6 Z, l - memset(buf, 0, sizeof(buf));0 N1 |5 M- l( Y5 F5 k
- ret = read(connfd, buf, sizeof(buf));+ J5 j+ h4 H* }) \% d( u0 S
- if (0 > ret)
0 b+ }, W: z" K1 n - {5 y" t3 \. r' I$ L' ~7 I: u
- perror("read");; {( U3 T1 U8 V i2 b
- break;
1 R' }" u+ D1 ~# h7 s" N. Y+ S - }//执行while循环读取数据,当% C$ I e/ L+ ^% G
- else if (0 == ret)
6 h$ l+ ? d9 V/ G. g - {/ j( ]% a Z+ f% T' H9 U5 C
- printf("write close!
; l0 q& \, M2 z i C - "); R" O" y- j. f2 w; z
- break;
$ F0 _* E$ a6 t. J2 ]! f - }; a6 B# F$ v$ b$ L( Z- ?& ~! B
- printf("recv: ");
! r9 u% W, M6 j" H! E* u. S9 ] - fputs(buf, stdout);//打印接收到的数据
. P) H; t* y3 x2 _! v" M - }0 m% \& L# p* x, p) \! H7 c, i; ?6 l
- close(sockfd);//关闭套接字) l6 ]+ @2 s$ [% [! I* x# Z0 o
- close(connfd);//断开连接* Y( h7 t. f# ?5 @( t/ p1 E" O
- return 0;) P4 w2 D* u5 ]; m! g
- }
复制代码 ' E \2 ]9 O- i- y0 L
% [' F- n' c4 F5 g/ A- /*客户端*/(具体功能和服务器一样,所以不再加注释)$ C' C1 A' D) C0 L5 G
- #include <stdio.h>! P6 J- ~3 K' M5 X
- #include <string.h>
. K Z" S$ S8 E% W - #include <stdlib.h>/ q8 |( ^' f& p6 ]9 B3 M
- #include <strings.h>
9 ?. C( Y. W; X, `' y* c* `' s - #include <sys/types.h>3 @2 h! B. V3 e7 ^8 N* g* s
- #include <sys/socket.h>
5 I% N9 X7 A8 v- [5 u - #include <netinet/in.h>
" s: @* `2 T9 F5 ` - #include <arpa/inet.h>. c0 E; `* D, e+ I' F
- int main()
0 A# G+ f( c% v+ U - {
$ w) k" C! y: g0 w; Q6 c3 f - int sockfd;# j* a5 b" a- L0 l" G
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))" \: n K& O: }' o8 Y
- {
0 R0 f+ e2 s5 Q' P$ z3 k- X! R - perror("socket");! J. r: ]5 i1 v" m/ f! C& ^' N
- return -1;
7 {8 b% b' m; h3 v) C5 ]3 C - }
3 @$ ^8 S+ e3 _% c: H! v - printf("socket...........0 h9 M/ ^( i, m( s
- ");
# [! _" F# S8 N' r: o. J9 X: m - : N+ R' Y! G: l# g/ o: g& Z
- struct sockaddr_in srv_addr;
/ N/ P- } i5 [$ ?* L: n - memset(&srv_addr, 0, sizeof(srv_addr));/ P' O$ u" [3 n
- srv_addr.sin_family = AF_INET;7 ~7 z* W' g9 G* Q
- srv_addr.sin_port = htons(8888);7 e2 S, g) Q. z' J9 }( E
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");. D/ ]. v, Y% z( h
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))8 S+ w+ |+ L4 ^) o
- {
R3 Z3 ?8 E; \9 Z - perror("connect");
& x t( X0 x$ z - return -1; //exit //pthread_exit
, R8 r' }0 J: f3 v - }
* O0 _) D5 o& S: P" h - printf("connect............... _0 A0 n1 @/ ?7 ~2 X* C
- ");
& t1 B3 ~3 p" o6 T) L, |8 ?3 f* R - char buf[100];
0 m8 l* c) {! j# N7 `7 R$ z0 W4 i - int ret;
6 D. l0 M$ q L4 n - while (1)& M7 L4 g _5 b" F* x8 h' b
- {" j6 G5 W- c1 Z/ u( Y$ K
- printf("send: ");
7 y: K' X6 X8 ^) Y! \2 ?+ U) \* Z - fgets(buf, sizeof(buf), stdin);
; C7 o3 G; F u2 t4 U: w - ret = write(sockfd, buf, sizeof(buf));
* W1 m/ _) N/ v$ G - if (ret < 0)
8 G, r% }( Q5 q5 {* O - {
# O/ f7 v- g) C8 \$ t6 x - perror("write");
" X) r7 M) |3 b( H" a1 u4 {8 s - break;( |( q4 n1 _% V8 {( S# G/ \5 l9 Z
- }2 {( l H+ R/ Z% m
- if (strncmp(buf, "quit", 4) == 0)4 Q: @; {6 m( Q/ S
- break;- m6 O, P& ^( h. d
- }
% [- \/ b# p0 }6 Z - close(sockfd);+ s4 u9 k6 |" J! w2 |* z
- return 0;
9 @5 z. i9 S% ]; s& M- m ] - }
复制代码
8 [$ a- | p5 q* S0 ]6 V* t& g; E& G8 z$ _ y( s7 P
|
|