管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
3 Q: t- e% i% B. E# x
# s% c; r g2 J% S0 `$ q9 V$ k0 q" n3 S8 B( L2 ?6 e$ C
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' ^! y: X" {7 W3 e" m5 z
+ V) x! J0 i3 J* E) j. `8 A& n* V
TCP协议
% ?8 _# c& ?- o- x/ O# TTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; P" q0 S( n4 s c* H0 V. O' \
9 ]. V" Q- } ]! d0 L q
" \- e+ n+ X' n8 B a关键词:三次握手,可靠,基于字节流。
+ H; i, q% I7 \4 E
3 i: m. O/ ?# d# a9 ? g0 k+ v; e+ Z2 D3 d' s( Y
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。7 ]9 V; g& i+ S; O5 f9 T% Y
+ }( D: g( \$ z* K: d. I. LTCP服务器端和客户端的运行流程7 @* w% h/ R# _+ Y+ c0 L, H3 \
8 E6 p; M, W$ C: r. u
1 k; ?( @ M5 f6 `) h! u如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ E! w `8 X! M/ p# L+ x# y- ~' q' q
( p2 q- ~( ~& D- l- i* _$ z0 s1.创建socket
. u' g6 r. x) Q# o1 U' c. X' Y; M socket是一个结构体,被创建在内核中
8 B3 a6 _4 d1 B9 o7 h sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
6 w8 `& e& ?3 I! Z$ [, i. N0 }/ E( ~) @# v+ }0 t
3 E9 ~9 n1 D0 u- d& l. i2.调用bind函数+ Z& b; A( M3 ~
将socket和地址(包括ip、port)绑定。
7 `( L& w) \) ^/ v 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
& o/ z ~/ ~( C struct sockaddr_in myaddr; //地址结构体1 ` D( \: r$ z* v; P9 L6 O' P
bind函数
, e7 I% a$ Q! H9 ?* p: ]2 B4 [" u& l bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))2 D/ q; ?4 j3 P* U( @: D
: F3 C6 F7 C+ Y
: o6 n3 C* P4 G$ g, Z( w! q5 ^$ a
3.listen监听,将接收到的客户端连接放入队列
8 ~3 d/ T3 v4 f- d- m$ M" e% b. D4 h listen(sockfd,8) //第二个参数是队列长度 b( q* @% b! {
# P5 N1 g. j+ u. m8 D
/ D- s: x1 v6 }! \1 C, V
4.调用accept函数,从队列获取请求,返回socket描 述符; D4 N' P, ~) j/ n8 R6 Z% C" n
如果无请求,将会阻塞,直到获得连接, M* j+ A* w7 m2 _* K) _6 I& s
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" W4 C6 E. X7 M _* `- J9 b+ J# f( R( j! H
) Y) w$ r; x& N( `. s5.调用read/write进行双向通信
4 J+ V2 q( m$ R2 D8 @# S; x: l" T& X# A \$ O/ _
$ i9 j3 w. \# A y" `6.关闭accept返回的socket
1 {. ?- F% O6 w! @# e7 i: m close(scokfd);
( ^4 d, }* e) n0 Y% N2 H, F J
# @1 I9 Q: |6 `* F# m. F3 Y9 X. l. \' Z: K, O( r: w
+ [: g) D% |2 r- _3 p7 F0 w7 G# t# P! P% w4 ^' F C3 b6 `" u0 h
下面放出完整代码
. X% G) f+ m* Q/ Y5 I
$ I9 [' M! W& r, r/ L, u8 w" D- /*服务器*/# V6 `5 ^' h: |
- #include <stdio.h>4 h" m2 W4 p6 s; t( T" A
- #include <string.h>
; b7 P) f1 ^5 Z( [9 N8 \; I( K# x - #include <stdlib.h>" O: Z) V2 {! J. f
- #include <strings.h>
5 X8 ? X' V; I1 l! L+ Q5 h - #include <sys/types.h>
7 c3 }$ [0 X# g* [ - #include <sys/socket.h>! R9 |$ k+ Q' [) X: H }
- #include <arpa/inet.h>
& l# t6 l& W! w6 f+ h - #include <netinet/in.h>+ S+ M A4 \; x" j. t- b* o
- int main()( P3 y' l) p; F" f
- {+ T& T3 m' w& N
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
& X" |- }6 A8 c+ F. F - if (sockfd < 0)( M9 n9 [; y5 M+ Q' V; e/ y
- {
; X% w S% z0 L+ N - perror("socket");
) q" T* B" S( h - return -1;
* x Z# r4 o3 A4 w9 b. [5 n - } //创建失败的错误处理
! B8 g% F/ E- i. c8 N - printf("socket..............
4 z/ Q) M' H6 z6 z - "); //成功则打印“socket。。。。”* V# H1 ~3 S( D5 ]( J: w0 u
-
3 g1 ]' l$ o; X - struct sockaddr_in myaddr; //创建“我的地址”结构体- W& U/ F" Y' Z' }5 C
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
2 S$ j6 m: Q1 u - myaddr.sin_family = AF_INET; //选择IPV4地址类型9 h, k% I; o8 T2 G6 W L6 ]
- myaddr.sin_port = htons(8888); //选择端口号
8 t0 }" X# _$ b! R: I - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
8 T0 y5 W( n- e -
* d S( H0 I/ M) K1 V1 A) l. x! O$ P - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
; R0 y" k+ w: g5 R, F! F8 N - {
& s: y- _- s; ~+ ]5 i - perror("bind");- d& t# G1 G; C$ t3 k0 ?
- return -1;
9 y% L5 A7 T; z - }
/ k- P$ E \4 R6 a8 r - printf("bind..........3 q1 T$ ~/ t6 W1 e8 E) K
- ");
' A; m! B. Y) D- ?9 s, @ - , _7 P% U2 s( J" `% F
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
7 l* i: M/ K V$ |# } - {; o3 v0 o' h/ n/ ?) _& a
- perror("listen");
/ M9 a8 T: q% g( } - return -1;4 [1 G5 ]" |, h, L2 R5 Q2 M0 {4 {
- }9 n! f8 e2 `/ R, g( e6 l! u7 _/ ^
- printf("listen............" C, J) v3 |7 T1 G
- ");) d, B6 c! J# m/ {: X& I. v
- 8 L, [4 D; f' [% [
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求! m, X6 h7 _9 G$ E; z* l
- if (connfd < 0)
# s" b# l% a+ S$ b% D - {
+ @3 u7 |- b8 t# \0 Y - perror("accept");/ |* q; c# v4 `- U* p x" {9 u/ c3 q# ~
- return -1;4 F% c1 [8 y" k$ q. O/ K& p
- }/ b5 b! ]' L' r e# n, y! O' p: q
- printf("accept..............3 { ~% E* X, M8 `# B) U4 c
- ");3 w( w4 @% s7 `1 G }/ a6 V4 R/ n n
- char buf[100];//定义一个数组用来存储接收到的数据9 w1 T( W( \- D9 H4 D# X* s
- int ret;# @! y; o+ U# ^8 U) V( W
- while (1): X2 q" U$ G" H1 D1 \& r: T
- {- S0 `# P- w! m9 Q7 n' V# S' U
- memset(buf, 0, sizeof(buf));
, K$ T3 }1 S+ O# j+ p& o - ret = read(connfd, buf, sizeof(buf));
s) J2 N1 N2 B* R$ x$ Q - if (0 > ret)
- o' e- @4 P8 c6 p - {& i$ G4 Y, v- j2 d" G+ H+ R
- perror("read");% p& M! C/ }9 R
- break;$ M _; f* x) X0 S$ [0 \" j* b
- }//执行while循环读取数据,当/ n1 |8 x. A2 y( p6 g, v3 k& p# n
- else if (0 == ret)
5 u( o z" W; G2 u- Q5 N* l - {" s% k2 Z! o* J
- printf("write close!
" h g- ^- W9 t1 Y2 ?( W/ X8 s - ");
; D% S. r/ o1 B; F( W9 G2 q - break;
$ J4 w4 B7 d/ s- u4 n5 [6 N - }
( P) N+ b* Q, o8 p( w3 [( e1 V( K - printf("recv: ");8 i# K# t6 k( t- A) ~) A K. M
- fputs(buf, stdout);//打印接收到的数据
+ s& E; Z2 l4 F- G5 b0 M: k/ A - }. y0 d. D, M% Y4 S( Q; D
- close(sockfd);//关闭套接字
2 i4 H: }8 x0 J8 }. l/ {8 p - close(connfd);//断开连接 d2 V4 _+ I* d4 \% L
- return 0;
" |7 I7 z! t! w# i) d - }
复制代码
5 w6 H" J+ L8 Z& R2 [$ d- W3 H. L7 u' b J: Y# R7 h4 I& y; z
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
2 N. M% `2 I4 r! i/ s' Z8 a - #include <stdio.h>8 z. w# Z# ~$ c/ V. k& [7 [
- #include <string.h>
% [3 u @; ^7 p- ?( K( R - #include <stdlib.h>, h9 ]7 _3 b6 u, E5 w. `
- #include <strings.h>
6 Z; X9 [8 Q% i) W - #include <sys/types.h>2 u( J$ o5 T. L+ X4 o
- #include <sys/socket.h>3 }! B: R7 H; P+ J) P: }6 s
- #include <netinet/in.h>
( @7 v' O7 u8 R/ z. t* P4 z - #include <arpa/inet.h>/ V, C3 k! k' T) m. N
- int main()
( I$ n. A- W1 E# R - {3 N% Z# A2 }: h. B0 n" T
- int sockfd;
1 M5 K* Y2 i; z& ~# g1 ` - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))* x t* B/ n* ]' e2 `3 g
- {& f# d& N: b6 M$ J+ f- p. @& O6 h
- perror("socket");8 U" o1 _3 T( t1 a1 a
- return -1;
( u4 \% q; U5 `6 s7 r9 q- p' r \' f - }
8 N' b$ o7 G4 {& u - printf("socket...........! J/ h$ ~# V% y2 m3 N& g* I4 R
- ");
* A+ A. A; {1 O5 P - ! U$ e" V. @1 r& {% {
- struct sockaddr_in srv_addr;
: H* t' U, _; \2 j - memset(&srv_addr, 0, sizeof(srv_addr));; F6 c) J3 {! j ^. l4 u7 z' o
- srv_addr.sin_family = AF_INET;9 z! b' _9 z: b* Y8 K
- srv_addr.sin_port = htons(8888);! R7 G$ @ Q: L
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
+ n: k9 s% n2 o - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
' @) z. Y8 z8 R- N7 e - {% c: s* C' p P3 |
- perror("connect");* E4 J( U. Y f2 ~. T; V
- return -1; //exit //pthread_exit5 ^& k/ l' J3 \/ R/ ^! o% }( K4 o
- }# L* U* ]4 |4 w2 Q9 k2 h4 M
- printf("connect..............
! N) W* E: y) x+ Z - ");2 G* K) q7 Y) k% u/ e& v- o
- char buf[100];
' h/ C3 T0 W4 [. ~( J" Y! f4 `$ l - int ret;
: n# H, c' F1 S0 ] - while (1)7 N) O/ ~) K8 r; g- [/ C
- {8 Y L+ t- e `# ^
- printf("send: ");' r0 C7 m' E7 o. c9 D* {( @
- fgets(buf, sizeof(buf), stdin);
$ l( ~1 T" p$ r& k6 w - ret = write(sockfd, buf, sizeof(buf));* M5 b, \4 i0 x, s; p' \
- if (ret < 0)0 I, F+ ~. b0 U2 m
- {9 g( [% y* @& Z* N8 M: G. _
- perror("write");
# q }4 z. G( }9 R/ l: j& a - break;
" \9 f5 }7 E2 j# F/ u% x) V# e6 y - }
7 R V3 e) f3 Q9 y% I5 q - if (strncmp(buf, "quit", 4) == 0)
% H$ {' w3 ~4 m, K8 f; L - break;0 s' N' A. v$ N2 B
- }# ]0 \" {" T6 a( \5 e
- close(sockfd);2 E M: j% `! t( O- l( a% y
- return 0;
3 ]* y n \. s, }+ _ - }
复制代码
# ^/ o. w4 b7 K' O' T
9 C3 l ^+ j/ u3 [6 L3 f |
|