cncml手绘网
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
[打印本页]
作者:
admin
时间:
2020-5-9 02:09
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
3 B; L/ O/ j$ M- D' M
, e' p$ z# {1 v- a; M& _
3 S- p- D0 C3 b! x0 w/ y" g
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( x: q# f1 n" J! i/ @7 C4 r9 O
& } r0 I8 D. y( c2 K8 m1 L. {4 C& @) e
. s: J; w3 x8 t1 c3 I9 k
TCP协议
. ?! X5 |5 u- i; W; G' g
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( c: r& V7 D) a' V; \# N
4 E5 _; S; K0 M7 X Y' S
! c- I# Y2 b( |2 ?
关键词:三次握手,可靠,基于字节流。
. S# X0 W) d9 p" |4 T
z3 C6 H! p W! h; Z
/ a. I; h Z* H" l
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
, Y j$ t6 q# c
微信截图_20200509015654.png
(175.4 KB, 下载次数: 8388)
下载附件
保存到相册
2020-5-9 01:57 上传
5 h0 T( H7 S( u
TCP服务器端和客户端的运行流程
0 D2 }$ ?& b5 u8 o' B
' {+ ?+ e, f* e: A; v
5 H3 }7 _* F9 \6 q
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
' u5 i5 t( G" o W% ^1 o2 Y
8 j: j% ~. K* `, E: Y
# K+ p+ e2 o4 Y g8 N
1.创建socket
5 C- n: C( [3 U/ ~+ O( q
socket是一个结构体,被创建在内核中
, M( [; m, s& A1 g% t2 ^/ s
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
U+ p/ x. r$ M4 W* m1 }
& \' j' e3 t8 d& b6 v2 _8 e
; a! E% O! Q& e j" q* S' [- [$ P
2.调用bind函数
; d) _0 j3 ]. Y8 a1 o2 i
将socket和地址(包括ip、port)绑定。
F& \$ |( O' ^4 T
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
& \* [: V* h- N$ S4 o7 g
struct sockaddr_in myaddr; //地址结构体
+ t7 W+ H8 `5 Y8 U9 g3 k
bind函数
8 w1 v% Y3 i9 E: j/ e0 G
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 Z# H8 d# V, A: z3 k' r
/ W3 Q% s+ `5 G# F# k! ~1 o3 V
6 ]3 i: }, T& ~0 \$ x( x
3.listen监听,将接收到的客户端连接放入队列
2 W7 m# o! l2 D& g' ^" ^
listen(sockfd,8) //第二个参数是队列长度
R) Y+ ]6 h4 c5 b
7 Q% t8 Y8 ~ Z, i5 `' O
' c7 v! n6 f& y% n4 E
4.调用accept函数,从队列获取请求,返回socket描 述符
3 r$ Y2 I) \( s6 \( g+ N2 `; P
如果无请求,将会阻塞,直到获得连接
; I" g# `$ g% M x
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" w( ^7 G: I" F+ |4 o ^$ l" ], H! ^
' E( Z' U0 d3 C8 }4 M
. g; y( ?1 `& ^3 H
5.调用read/write进行双向通信
" N( y- v5 n+ f. ^0 C; l/ v) w
: G9 _8 m2 _+ e) w) K/ B2 F6 J: j
1 ^4 W' G( }/ l$ h1 Y6 k7 i
6.关闭accept返回的socket
$ ^* i1 K* i% h2 e( w' N% p$ s
close(scokfd);
, J F; F F! K! z/ i
9 m" W) ^& j+ |3 L$ w' _5 d
7 L% r. L2 X$ B9 r
! h0 p! y9 m E, G
7 B6 Q1 D7 |6 K3 [% y& A9 r
下面放出完整代码
8 [9 o) }' Q" u* ~( d* s
# t2 g5 O! v6 t
/*服务器*/
6 u1 }! [7 w4 F, | |
#include <stdio.h>
/ k M0 x9 G& x1 {% Z8 X3 O
#include <string.h>
5 D; T4 S- t; g1 y- D! k; |5 y
#include <stdlib.h>
" W2 }' d& C3 b# F% W
#include <strings.h>
7 A; X: c" d5 ]7 R% F6 c7 N2 p
#include <sys/types.h>
5 o+ O+ W, |% Y% b
#include <sys/socket.h>
* ~9 t3 O5 Y( m( P
#include <arpa/inet.h>
+ \% p! v3 v7 o) s9 g
#include <netinet/in.h>
# ?5 L0 @( i% a5 _1 J6 o, c) q
int main()
( ^$ i6 k9 g9 t! U
{
2 t+ Z# W4 Q9 r# E* w
int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
$ A' T6 C/ f2 B
if (sockfd < 0)
3 l% i1 @( Y4 S! i
{
8 e1 u0 a2 _7 l8 p: d
perror("socket");
; H. n3 L n, z! b2 J+ o
return -1;
4 j: m: p7 d! I% A" J
} //创建失败的错误处理
& E2 k# @. Q2 ?1 g! G' e- t
printf("socket..............
& B* Q ?9 o) }" Q- O7 w2 w
"); //成功则打印“socket。。。。”
8 O" T0 `, l, H2 q; w
) e8 h1 ^6 Z- ]- _3 b
struct sockaddr_in myaddr; //创建“我的地址”结构体
9 U0 a. t- e/ o, }7 A; @! P
memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
: _& y$ P8 Z; Z/ Q! Z# l8 q4 `
myaddr.sin_family = AF_INET; //选择IPV4地址类型
, ~3 M4 t: W; X; B0 i
myaddr.sin_port = htons(8888); //选择端口号
8 O. s4 O1 B( f& _- ]
myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
& u7 G9 } p3 O& l7 J# E8 {+ L
; D+ u: ~8 x" J& {$ J8 F) X4 {
if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
/ g$ v+ D$ L2 X2 E9 G
{
! I, C( d$ G+ {' M: U
perror("bind");
: M2 [5 b }& t8 H) q" [; U5 \& P
return -1;
$ W7 ~; N( B! E V$ T5 Z1 [9 w: j' P
}
( E. r* u, s3 ~
printf("bind..........
4 I- c$ v4 ~0 w; v' G
");
8 D5 q! t' q, v) t
9 Q4 \8 p, p, `3 U7 A
if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
6 Q* g& @# @: i
{
- |3 b- K7 r$ V" \" g
perror("listen");
5 f% n: m2 n$ F
return -1;
" K7 G( W, m- `6 ^1 E1 t
}
/ h! W, _$ `2 z( J8 f' T
printf("listen............
; Y- y' U* f8 S* N' B( g: l# S
");
- R* p5 c& Y" |' G/ t2 c
+ A3 q* S- B8 T; D- o
int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
+ Q0 Y6 M4 R- k
if (connfd < 0)
7 m6 H7 L3 F$ l" q; A; {+ r
{
# ]0 E; P( y, c( L
perror("accept");
8 K2 p3 L. ]$ A9 _( U Y4 \; v
return -1;
" O/ p# \8 w# ?* Q5 d" l
}
6 ~/ Z9 {- C2 B! ~
printf("accept..............
* }- U; B# u# e& ~
");
, r3 P8 A1 z; [+ |/ a/ M- }1 i# S
char buf[100];//定义一个数组用来存储接收到的数据
2 @. X" R; R, G4 C0 h" l! E
int ret;
: k" W" I' Q8 Y/ D7 m4 u
while (1)
2 w B: @; H' _- Q$ b
{
3 {" a8 Z3 F) u9 O
memset(buf, 0, sizeof(buf));
+ I( h6 _, B+ _2 X6 }9 T7 m, u
ret = read(connfd, buf, sizeof(buf));
: y! o7 c0 b0 r% s d8 b! m) v! [- N! K
if (0 > ret)
7 z7 y+ O; V; ~. r- Q& q# U
{
5 E1 L* `) Q8 h" N9 X. g8 E. a
perror("read");
/ {3 f1 Z' o1 K
break;
. N7 ^# N) X7 {9 k2 O
}//执行while循环读取数据,当
9 n* c6 N' R) N7 t' d5 V2 @
else if (0 == ret)
2 ?7 _1 A' a7 N, d7 ?. F" e
{
, s2 ~5 x- r! x, L( R$ s g Z
printf("write close!
3 B! M L9 g6 i8 h# ?
");
, x/ y$ e+ i5 E% k
break;
1 v. B. B; M; w! h/ m1 l# H2 u& ]
}
) E# u" s0 }2 | L; L3 p& r
printf("recv: ");
" N8 r [- p/ f u
fputs(buf, stdout);//打印接收到的数据
+ A4 S; P* O6 {$ ]) @1 }
}
' m4 v- s0 o% t
close(sockfd);//关闭套接字
8 s8 E5 V3 K, C7 `- N& U
close(connfd);//断开连接
6 }+ s' X$ d& X2 s) J8 ?4 @! T; _
return 0;
W1 u1 Y8 T+ w1 v9 N: r0 ]
}
复制代码
* W# n8 K- b) B5 x" A7 @# ~% c
' L. I8 I2 e" Y6 Y( H. b l5 Q
/*客户端*/(具体功能和服务器一样,所以不再加注释)
+ q* M! n' J* t* x8 y6 H
#include <stdio.h>
' k7 f2 K5 {6 h+ G. d: L
#include <string.h>
! P9 ~/ Y# O* [( J
#include <stdlib.h>
+ W% J; T" o" ?+ }5 K
#include <strings.h>
) d" d: F! H$ C7 w' V
#include <sys/types.h>
% s& s8 q& Z' y" b* s: [( U
#include <sys/socket.h>
- {3 |& `: h- k& ?# q! e
#include <netinet/in.h>
% g6 |1 a. @+ l2 y
#include <arpa/inet.h>
7 K+ }. b; N3 w8 L* F
int main()
+ \. V0 h+ F% `$ q+ l, s
{
; z0 ~+ h/ F# O8 V$ ~) A
int sockfd;
3 m5 s& i4 d5 R1 X& C
if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
) B* A1 G2 N# V' M. @
{
5 Y. c) `1 }. U0 L
perror("socket");
& x6 j5 Q5 F! _% x' @' ~1 @, E" r
return -1;
" Z, m; d+ m4 @) _8 ~! `( @8 m
}
( `8 ?% R4 o4 b4 I/ I( S0 i$ b: C
printf("socket...........
& C, g* y: Y7 O. f4 z" _9 V: t; ?
");
$ K" C% L, @) H; j
/ `0 U1 m- A" P
struct sockaddr_in srv_addr;
2 N$ K# P* o" Y/ H- D
memset(&srv_addr, 0, sizeof(srv_addr));
) v& J( V, h6 H& x/ ]' v0 |) j
srv_addr.sin_family = AF_INET;
7 C8 w: g; q1 ]& n0 I. s1 G+ C; A5 p
srv_addr.sin_port = htons(8888);
: R* ~8 b9 E. ]0 n7 u
srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
0 V8 W, P. p; x
if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
2 q" D3 H0 R* A) d3 ^, I
{
1 E! Z+ A: u8 _) H7 ^
perror("connect");
& _9 q% M' H4 D! c
return -1; //exit //pthread_exit
; c, c2 P1 r- F* A' Q
}
! z1 ^* b0 Z9 \* N T
printf("connect..............
9 m. c+ w" ?' k4 S& ]7 _$ x+ b/ V
");
8 _: n, D6 K+ v1 p- P( B
char buf[100];
0 J* m8 B5 A* u5 O
int ret;
! F6 J0 D% {7 k( [' `
while (1)
% F: `2 @9 e6 s6 O- P, C* V$ w
{
4 x- s* J. r2 y" Y5 N: ?
printf("send: ");
# c- C8 x3 ^' C2 `1 ^" u8 B5 l+ ^
fgets(buf, sizeof(buf), stdin);
* O; T( m8 i% Q/ C6 K
ret = write(sockfd, buf, sizeof(buf));
- g- S1 w+ n+ a5 O" I7 L3 K
if (ret < 0)
' \! W( l* k# y3 ?
{
7 p' o6 X% M+ J0 t2 k
perror("write");
( K9 J: U+ h$ k
break;
) ], Y: Z# }+ ^& O7 A; z( ]
}
# p! a' g3 F1 r0 R
if (strncmp(buf, "quit", 4) == 0)
$ J* L8 j- P$ r3 W4 h* N
break;
1 I. L' B2 u/ H5 d, [2 I; {
}
% K G! J1 E" y' y0 o/ y
close(sockfd);
9 E" |: K" t, b; f
return 0;
6 y( [& U$ V/ o J% X. H. x
}
复制代码
8 G8 d3 v6 t" i7 f( }7 ^" l; M: @
n& s! U* i7 _6 D5 }4 F8 G
欢迎光临 cncml手绘网 (http://www.cncml.com/)
Powered by Discuz! X3.2