cncml手绘网
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
[打印本页]
作者:
admin
时间:
2020-5-9 02:09
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
" W* C0 `5 M7 k. |( a( O. Z
' [3 _; y+ d: h% C* ]
) k+ C2 w: y/ Q$ A. y2 l
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" \* C2 Z4 x {7 l+ Z: Y
1 ?- [6 h Z( Q" D( L( c
9 X% O7 u9 _. x9 C0 R8 S) f; R3 R
TCP协议
1 M' l) X/ A7 o, Y6 q) b9 f# E
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( d3 u( _3 I" j- c b- a
6 D) r% [# K1 U
! r5 {+ e4 o, e! y1 W2 g; N
关键词:三次握手,可靠,基于字节流。
0 [) d2 r' C3 P5 X1 v6 l" _
$ {# ^2 u) l" f6 [
+ j4 @) |" h: K; [- P( b
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
* [ T/ a7 m! Q W* y: G
微信截图_20200509015654.png
(175.4 KB, 下载次数: 8931)
下载附件
保存到相册
2020-5-9 01:57 上传
0 j* n& m9 L2 C9 ] }2 F: K5 j; T
TCP服务器端和客户端的运行流程
! ]) v# S$ K @4 q7 x5 n0 m
; I, |9 {4 |" D# t
% n+ \. J6 p( {9 V/ {; K5 Z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
0 W, T. y$ o# X/ ~
' C4 U, }% e; }. g% I
* a; o/ n J$ R6 ~
1.创建socket
: k4 r( I( u1 E2 n. ^7 t
socket是一个结构体,被创建在内核中
% J) ~" v9 T( {6 U* X
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
! k, T2 v- y9 B2 c3 b/ ]; Y/ K3 D5 l
8 x" s4 o3 C& z- r& [: v
6 h' t& v! G9 k" R- a* g$ g; t; T4 G4 @
2.调用bind函数
4 K5 F! Y4 C* j$ V* k
将socket和地址(包括ip、port)绑定。
. h$ c, M6 _/ t/ c( Q, X0 \& M7 \, ^
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
' O2 ^1 J' N7 X) ^5 V7 X
struct sockaddr_in myaddr; //地址结构体
" s- m" q" y. d' a5 f
bind函数
e5 Z4 T' L4 [, {9 A" c; \8 e
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% o; W4 h0 ]. h
$ f. c1 \& F6 I, Q; p+ b
& U2 j8 N( W0 h6 ]* u: c
3.listen监听,将接收到的客户端连接放入队列
$ i. v8 q0 e0 W3 T$ `- x8 j
listen(sockfd,8) //第二个参数是队列长度
; E K) D1 n1 d: m! d
% a& n: F! h, b `) o3 H
5 s( ~3 R6 y) B5 M1 v
4.调用accept函数,从队列获取请求,返回socket描 述符
- Q# R2 C4 J9 D0 c5 p
如果无请求,将会阻塞,直到获得连接
0 _' c( ?0 @4 m- x% Z+ _
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
9 p3 B4 j ]* c6 N4 U
9 d& H; G; h7 Q4 G# @* T
6 _) N& K4 j' I
5.调用read/write进行双向通信
4 W8 v0 i6 L, P) K0 S: \! \
+ m" P/ o" t: T9 S% v
6 f5 T" ~% G6 Z. K( |
6.关闭accept返回的socket
5 r/ l4 N% G+ |% u
close(scokfd);
1 ?' j$ Z& X1 a; R1 j
" H6 x! f4 x& K/ @0 N+ ~
6 X3 o) |3 s* i1 V" m7 j
+ S. J6 K" ^% i; a! c4 E# e
) B/ S. q' L$ `7 u$ d& U7 ~
下面放出完整代码
8 [$ |; [( k8 o# s# |
$ P3 n# g4 O) i% Q0 c
/*服务器*/
( k6 M! `& J4 t3 [' L
#include <stdio.h>
' s6 K' ?0 A" q+ @6 R
#include <string.h>
3 V9 T7 [) K9 ]# W, N( z
#include <stdlib.h>
6 H& \! w* H$ n) |5 A4 P
#include <strings.h>
# l6 h8 @4 c( W
#include <sys/types.h>
; K! G+ x- w; G
#include <sys/socket.h>
: a+ n" w" p1 [' I9 E) _2 O
#include <arpa/inet.h>
- q9 u* f: D# j; X
#include <netinet/in.h>
/ k0 F/ Z: U% a* ]* G+ u
int main()
! |' d* N* n( s. U
{
V. V7 `5 x% u4 L4 P
int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
: w O% d3 `. k% E" m
if (sockfd < 0)
5 a9 g: q9 N. T9 z
{
, p, F+ t" x1 R# `+ Y' I
perror("socket");
3 {- ~. R, y$ F1 ^, n# G
return -1;
( |. Q- {/ x- r9 A6 j/ M Y. K/ O
} //创建失败的错误处理
+ D1 `. d9 V+ ~* E
printf("socket..............
$ b& W% W" q r( w' M$ {
"); //成功则打印“socket。。。。”
4 S3 d8 j9 h5 C8 j
+ v4 I2 Y: B7 u* N7 I7 c
struct sockaddr_in myaddr; //创建“我的地址”结构体
5 Y$ K) T' a+ e- I! n) b
memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
; }7 E# j6 u F& n: s" m' K) g
myaddr.sin_family = AF_INET; //选择IPV4地址类型
8 d6 Z5 K- Z* N; m5 ]6 o! W s
myaddr.sin_port = htons(8888); //选择端口号
" U, j: M0 O0 C( A/ W
myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
2 W( l0 I- ]/ R
" i4 d/ D( n2 ]5 B& V; ]) h7 S
if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
7 i4 K1 N: W$ q$ k4 X# W
{
& s2 ~9 _* E9 x! Z0 E
perror("bind");
1 ]6 ?! Q9 K) q% r
return -1;
- a$ a; X/ p- W4 C2 I
}
- a' }+ [+ r0 Z. S4 f! i
printf("bind..........
3 w/ r7 ~$ H- i5 U t9 d- t
");
" P+ R2 a' I* \; w# s- Z1 J' `& F z
3 ~8 H4 L6 H0 k2 x2 v
if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
, f# n+ j. r2 {3 X5 ]5 M6 F6 E5 F) @
{
% W: h T$ N) P0 ?3 q+ g1 R4 h, C0 P
perror("listen");
4 q( N, t/ c8 N8 v5 h- D
return -1;
7 G2 f# t# R% k' m" t; W$ V
}
4 l# Z4 ?$ _7 C3 n
printf("listen............
T; L- u! i- p# S: ^; Q
");
- _1 D+ ]$ a' e4 I* p
7 A+ l d; g6 V) ~: y
int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
5 Z0 q5 C1 [/ Z: L4 z
if (connfd < 0)
2 z* ?0 F" s( w
{
; C( ^) _4 \; y, i+ ^& m. Q
perror("accept");
4 N& A$ C$ W( H2 A
return -1;
# E3 K8 p3 e0 `' e3 D
}
) ]. W: \4 r; a* u" z9 k
printf("accept..............
* O2 y5 X% [: o7 n! O1 o- V
");
( l8 t0 N# A7 A& ~: M" z7 n2 g
char buf[100];//定义一个数组用来存储接收到的数据
0 r5 F. M( h/ t' c: \" W
int ret;
- c% w% f% b6 W% M) K+ f" w8 g5 H
while (1)
% i; Z7 U; g, W# D
{
) x( `8 t+ p p3 Z
memset(buf, 0, sizeof(buf));
8 a5 x4 B# G) W0 X5 G1 P
ret = read(connfd, buf, sizeof(buf));
/ S" { R1 i+ r# |
if (0 > ret)
5 Z G, q2 ^$ Y4 |9 p
{
) C% U$ c7 F, E: m- n
perror("read");
) p! b! B" E( N+ j( x; x
break;
. R" p7 D9 r+ H1 U
}//执行while循环读取数据,当
/ Q7 }$ X6 t* G% `& p
else if (0 == ret)
8 K ]5 S6 h! E
{
% t$ r/ B. [% u4 m3 B* _- b
printf("write close!
% R4 m( @: A) H1 x" m5 V
");
- u, C" N" a3 f; f8 y* R
break;
) B* j/ a$ b1 u9 T# U9 C/ Q8 r
}
8 M# B( ?$ Q5 d# }
printf("recv: ");
" L; H+ F- V5 N$ L
fputs(buf, stdout);//打印接收到的数据
! V l5 e5 t0 |8 s& I
}
% P, D1 | [$ V; f; P9 t
close(sockfd);//关闭套接字
5 n: B+ f( L+ Q8 i
close(connfd);//断开连接
! m* D0 }; a9 c6 m
return 0;
; l a* ~, t v( p, a; j3 J
}
复制代码
; S8 p) |, b ]- g1 \
+ i9 _; U) E8 Z0 m7 }* @; L
/*客户端*/(具体功能和服务器一样,所以不再加注释)
; F. G/ u2 z2 u* n
#include <stdio.h>
6 c3 b4 ^% y- T; Y- H/ C
#include <string.h>
5 c1 }, i: [ b& J6 b
#include <stdlib.h>
( X. {9 ] x4 N0 H3 ]7 R( t0 \
#include <strings.h>
# c0 T0 K+ h+ s( @1 o Q0 p4 ]
#include <sys/types.h>
; m; h/ L2 `3 ?; a# }& }0 Y
#include <sys/socket.h>
6 B8 S) b( b* w7 Y; V
#include <netinet/in.h>
/ e$ _6 p: N9 e x4 o
#include <arpa/inet.h>
# t, K g+ P6 L: o
int main()
1 d2 O, a: y, p* p( f- @
{
2 _) A5 s6 a* Q
int sockfd;
9 h) E+ H2 |' i D ^! [2 }# j' {
if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
# h9 G& Y/ o$ O% P$ F' P
{
- x% k4 P. C0 q: H4 ^: C6 t
perror("socket");
9 ^) W |0 B1 w% E7 {" I
return -1;
9 \5 V2 g" I$ ]; }6 g8 I* x
}
% [1 Y) K9 r/ N! l
printf("socket...........
6 m0 F4 p/ K' X4 ]6 O! h6 A
");
# P r0 @0 w& u+ E7 B! g1 J( Q
7 O6 N+ G( T" s' I c
struct sockaddr_in srv_addr;
. m C J( ]: y. I( y
memset(&srv_addr, 0, sizeof(srv_addr));
9 u/ ]+ N% s% @8 _
srv_addr.sin_family = AF_INET;
5 \; n& B3 {9 b1 v D9 w4 l' y; }. P
srv_addr.sin_port = htons(8888);
% r* U9 @4 T( D, g, Z) g
srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
9 Y5 h- Y3 s- |! U; v1 ]. v
if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
* a; n2 d* i8 t7 k. O) i
{
; Q- g6 }7 Z" \- i! L! y" ?& @
perror("connect");
( W% e, j" e: d) Q
return -1; //exit //pthread_exit
$ X+ ?6 n& q$ m9 K( O
}
! _( F8 X+ {1 h+ {0 R5 s
printf("connect..............
% b; Z1 k2 G4 P, u. l% `
");
. N& o: T* i' p0 K K0 ^
char buf[100];
2 F* Y( e# a& v* B+ h
int ret;
, x8 J$ W% ]* q$ q* h( a( i8 a0 K) t
while (1)
6 F+ i2 e/ F' o5 G8 `
{
3 C# H/ X8 R) E) w+ M+ `
printf("send: ");
- P0 ^1 x/ H" _. x
fgets(buf, sizeof(buf), stdin);
2 w0 n( V+ L' ?
ret = write(sockfd, buf, sizeof(buf));
+ C, l/ q2 _2 w2 U8 N# C s5 K
if (ret < 0)
/ L7 z3 m K" ]* J/ J) c) [
{
0 i1 Q( c: ?: ?( [7 a- G( i
perror("write");
8 R" v& ?1 e7 y6 }. b Z; ~
break;
1 q5 Q4 g$ J6 F# p) X+ k: Z; m
}
& q8 i5 u. _) k) ~+ v- U3 h$ B3 O
if (strncmp(buf, "quit", 4) == 0)
$ Z4 K( m" p% _1 r+ k1 Z% L
break;
* j- k0 x+ P( v, Q: @
}
7 g* w$ [8 Z, k7 e6 \& R' t
close(sockfd);
7 A: i( h4 @. B4 X2 J
return 0;
) C) b7 h$ C9 [1 g
}
复制代码
/ g, M0 ^ e; I7 E6 o3 l2 F
7 `/ F+ d) [& k' t: z G1 V
欢迎光临 cncml手绘网 (http://www.cncml.com/)
Powered by Discuz! X3.2