管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 O- G' m% {' s% B4 ~6 y
; `4 s8 u" K0 s! {0 d0 I$ A* L* |! a. s' N
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ ^ z, @& m. f9 P4 `/ l4 I& A6 }% W9 V4 U
$ X% n9 z. y" K7 {; Z# a5 l% G9 h
TCP协议0 f4 \( ?8 R4 y: k
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
+ }" _! z, E9 M' K X
# i6 Y/ f: ]' B( P) t1 ^7 G& H5 b# y/ b5 J( ^8 q- r, R
关键词:三次握手,可靠,基于字节流。
9 B, f, S: N4 Q% H7 a
9 g/ `3 Y7 L) ]& E! _, b [: d8 h3 ^" @
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
* w3 |+ x7 l4 h' T4 S: |! A6 B0 y
* W) M! P6 Z E& q" i# h8 g/ hTCP服务器端和客户端的运行流程5 J% R$ H7 I0 c" z A9 {
: m. |+ a/ W" \2 \9 S5 M* y; o9 W8 d* W4 D0 ?, X# N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
$ m% c' t6 q$ r6 E! R) ^1 I8 n. x5 q; h7 D7 N6 T7 c8 a" W! r
& B" {9 q5 j% X; ~1.创建socket
3 y( l _9 |3 W1 ~ socket是一个结构体,被创建在内核中- z2 ]9 [9 N- I
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
4 S2 i0 J- k% y7 @; }) I6 i) k! q6 R( S3 K2 b# A2 g
( ?- H: M3 D% E1 h# N( Q
2.调用bind函数8 C, t- r0 `" @+ b; Y
将socket和地址(包括ip、port)绑定。 H% i* u7 @7 Z6 r6 s! J/ ?
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) c0 _, v8 O: v* f. t
struct sockaddr_in myaddr; //地址结构体
4 V) \1 v, u: y8 h# b bind函数
) E% b6 i& a0 ~0 A' x1 A; V, I bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* T0 C/ y; v* I2 o# C: S9 S
6 b' o" _6 ~1 }5 W* }& @
! e! N3 \: e1 ^ q" `5 R7 T% Z' I3.listen监听,将接收到的客户端连接放入队列6 F: W" j$ K0 u2 ~" @$ t: U
listen(sockfd,8) //第二个参数是队列长度
. k; W7 C) e# h* ?: s$ v+ r$ j. E# K
2 w1 Z, T: U; d& E6 S; F4.调用accept函数,从队列获取请求,返回socket描 述符
. N. K* ^+ Z9 ?, M+ Y# | 如果无请求,将会阻塞,直到获得连接0 [) u5 d. O$ F h, x
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数2 \) a# s/ U* _
. k* ~% c, j1 y# c/ i0 v6 ?5 Y
8 `, C( v. {9 z' x" T% y& m
5.调用read/write进行双向通信1 k% S; K8 {" o6 J; ~
2 b6 I. u: v, u, L
0 m9 \& r1 v3 W. I6.关闭accept返回的socket
( ^2 V( Y& E) l$ u# k- V close(scokfd);
- z; a" \. S6 l! V2 O# O9 H0 k( k# H1 {
; t$ e3 H; ]+ w" x. b: K; N" G# }) M, i% x
, c* W0 o# C8 D6 h4 K: y' @6 P( n
下面放出完整代码3 n \! n& @2 {) Y: o/ w) O* J
# ~! S0 U1 Y$ t8 v: M" {; A
- /*服务器*/9 R9 k! j" X7 r2 G( [0 ]
- #include <stdio.h>8 p/ a, T1 `4 k7 Z, N; N
- #include <string.h>
& I/ z/ a: E9 p- ^ - #include <stdlib.h>
+ o5 B6 {5 I1 h6 l5 r - #include <strings.h>
' |0 J& m5 z7 b7 r* W. o - #include <sys/types.h>
7 \0 L v! {9 t( D6 }* J - #include <sys/socket.h>
! ]$ x; _" ]5 u' F - #include <arpa/inet.h>
: P8 w; L( `8 z. i' i# o - #include <netinet/in.h>
% m ]9 T/ A& u; C/ L5 }: \ - int main(): K) l9 o: X) T) b
- {8 W* {" o' j5 e
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# `% r" S0 H4 ~- `9 F
- if (sockfd < 0)
7 X) V: {, g( n8 e" L% z5 b+ a- @ - {8 g- b( o% M' d: W# |( [: p
- perror("socket");
V4 W5 X8 X9 E: R+ J, w# S, O - return -1;
. ~8 K* k% o. `: y - } //创建失败的错误处理, `6 z! E6 |, V9 N2 s
- printf("socket..............
$ D1 [+ p3 f2 R9 { q - "); //成功则打印“socket。。。。”
+ Z+ @. E1 ~7 R" D -
. C) z& a. ~0 [" A# G - struct sockaddr_in myaddr; //创建“我的地址”结构体; ]: [1 R3 D( v2 a' }! z5 u5 \
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
; _5 y! {4 H) u3 p- n6 e; j3 S$ y - myaddr.sin_family = AF_INET; //选择IPV4地址类型
+ {/ L/ h8 W3 F ?& g K0 ?6 j - myaddr.sin_port = htons(8888); //选择端口号
+ |' [1 T- G: S+ W4 w" z* T1 } - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址! B* j2 R% e4 j0 [7 c& r
-
- m1 t' K+ S& g- V- T) n% E - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
9 }6 \. W/ k3 q% U8 f8 d3 K& ` - {- T. `! h2 {4 B' P) \
- perror("bind");
- _. f# ~9 k3 C% A; [& R: g/ F - return -1;8 T/ o8 y! u% O% y4 Q
- }
( r. l1 e* z; [; q- g9 z2 z - printf("bind..........$ N2 W0 [# q3 _2 |) ~& t4 o# i, u
- ");
& o! e o, g( r1 x. T -
3 Z8 H! @: Z0 z# a, Z( |$ \, k - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听6 n, o) A0 @- A# `
- {/ c1 Y* v: P7 S; r
- perror("listen"); @* }& N6 k+ E2 i
- return -1;9 x& W1 d* d: y( t f+ B
- } Y" B2 ^; C& F
- printf("listen............
; l% P5 m/ S4 M& C. L! n% t2 C - ");, r4 f7 y; s* w5 W. `/ [3 T i
-
6 a/ b \0 h% ?$ h- s% K- P" ?/ K - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求 H' B! T# j* e/ ]% o
- if (connfd < 0)
' V1 }& G d b Q6 K - {
, B( @. M' `" T! _ - perror("accept");9 Z/ b$ N5 F) v. }! F
- return -1;7 p; O) W9 E. W7 m. Y! a
- }' I. j' T" p- o/ C7 h5 ?
- printf("accept..............4 W. B& r% i3 K5 b, u% V
- ");
# ? Q2 C( z5 R6 ?) W - char buf[100];//定义一个数组用来存储接收到的数据
9 y6 z# M" x1 [7 z - int ret;
# x w1 \" z: M6 A) s. b; ` - while (1)
- o3 F3 h$ ]' G4 D _) Q - {
4 S: w& i% {' X1 p, \% V! H9 X - memset(buf, 0, sizeof(buf));
+ _5 X! I9 U/ Z+ L% h! Q+ m2 } - ret = read(connfd, buf, sizeof(buf));
) b4 \4 j2 \. S - if (0 > ret)
9 E7 \1 P6 ^3 P |5 W1 | - {
9 r/ P5 n( d+ { - perror("read");( U' e) k/ f/ C$ K$ z* r. j
- break;
5 l D5 r# p) R7 M& c - }//执行while循环读取数据,当: m) E% e) [9 Q
- else if (0 == ret); l, \ L9 r; D2 ^
- {
9 G( G' e+ F( v) f5 J! A+ l& ^ - printf("write close!
$ h5 t5 ~( u* @8 r" G - ");
1 ?/ n9 W+ I& m( x5 u( s - break;( j8 {/ p! e/ \7 ^3 }. B! W8 @% l. M
- }
* ^7 S# L3 C- L - printf("recv: ");- f$ m& `& N- T/ h: X4 w& K
- fputs(buf, stdout);//打印接收到的数据! H( M& m7 b% k7 }4 T' o- {
- }
" O4 w- B/ s5 \& e, K - close(sockfd);//关闭套接字
2 ^- u. \% N% v0 Q* n - close(connfd);//断开连接
3 h0 \7 G$ T9 j- |- m" F - return 0;
: ` N- i$ n; b: I$ O6 b! z1 Y - }
复制代码 " z4 e% Y7 h b- P% E
) r, w) T* B( Z2 e m- /*客户端*/(具体功能和服务器一样,所以不再加注释). q6 s+ l8 g& e
- #include <stdio.h>
! `8 s. [" f" K4 f" i9 ^ p - #include <string.h>
# W; m; B1 a. L e( [4 x8 E4 Q - #include <stdlib.h>+ f ^; M- n/ [, Z7 O
- #include <strings.h>
0 O5 ~9 F6 @5 D8 t L - #include <sys/types.h>
* L! e; ?7 m# V0 {- _0 Q6 F - #include <sys/socket.h>
) ^" Q- V& J- j8 t ~4 ]; Q - #include <netinet/in.h>6 A! _. y; b6 o8 {2 N# B0 G
- #include <arpa/inet.h>& ]9 G9 \2 m" H9 N, O
- int main()0 h1 u+ J( |, A& }
- {
0 I: R' M" ~0 ]0 @) |6 ] P - int sockfd;0 \' U; m8 o+ s1 |: g
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))): ]* j$ k! g. \9 @% X
- {
8 h: Q& M8 K5 ?/ I8 F2 f - perror("socket");
' }8 _, z- j: [% [ { - return -1;
@. k" a6 d, K' i0 y; b - }
3 j; H+ r! |$ j& m! _5 U - printf("socket...........5 L; \) ?+ ^* ^& z: q K
- "); J4 o$ J: o6 `: n' }1 j5 M
- 7 c& O' {1 \/ z) S" g, a8 x S
- struct sockaddr_in srv_addr;; b) K$ |6 S1 F" T% `
- memset(&srv_addr, 0, sizeof(srv_addr));
& j3 N6 Y) }! k8 W8 M$ ]' y) s - srv_addr.sin_family = AF_INET;
s, k5 m; }2 e' ~& B% ?7 h - srv_addr.sin_port = htons(8888); B4 X" E* G( b' S* }* h
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");$ E2 m% b; W" o5 _+ }
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
% s- M: I/ |; m0 K5 y8 U - {% T% v1 k% _) {1 S$ y
- perror("connect");
3 B2 I. N; M/ n - return -1; //exit //pthread_exit+ _2 _- {2 H, N/ R+ }- t& J% J
- }
1 T2 ~7 E2 q9 J& N% I. e; i - printf("connect..............# j' ]5 `+ R4 ~+ @' S0 d
- ");! `1 j; u, w& _$ O
- char buf[100];
4 S# H+ t+ n; ]& ~ - int ret;$ |3 P. @. K& g+ w$ Y
- while (1)' ]/ k! j- S+ T
- {
& {; s6 z) O. O( \ - printf("send: ");
6 @) ^# C3 D# O - fgets(buf, sizeof(buf), stdin);) j2 \/ ^7 O) W. N
- ret = write(sockfd, buf, sizeof(buf));% _: ]# T( G0 f- |9 W9 ~
- if (ret < 0)
0 [* a" P2 b- r0 n - { s$ L1 G+ R2 I! B) Q
- perror("write");
5 N8 P5 K8 q8 g3 _ e - break;
1 J' E2 E( w3 U$ y - }& y- T/ x: T+ p# j
- if (strncmp(buf, "quit", 4) == 0)0 Z' e! q+ f! o
- break;. D. _4 W4 t4 y* r( z& g) D% M2 U
- }: H4 t6 x/ q: s5 `
- close(sockfd);
6 ~0 d: X6 e* V+ a - return 0;9 ^ s4 i2 W' }; E* B! h5 H
- }
复制代码
) F3 Y" Z1 _' s. N$ g! m& c
8 m, V" n+ O4 M5 z$ P |
|