tcp服务器和客户端的编程流程:
eg:
时间/日期客户程序:
时间/日期服务端程序:
**********************************************************************************
套接字就是一个接口(本质是文件)
一、套接字的属性
域(domain)、类型(type)和协议(protocol)。
1、套接字的域
域指定套接字通信中使用的网络介质。最常见的套接字域是AF_INET,它指的是Internet网络,许多Linux局域网使用的都是该网络,其底层的协议——网际协议(IP)只有一个地址族,即人们常说的IP地址。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的IP地址和端口来指定一台联网机器上的某个特定服务,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。另一个域AF_UNIX表示UNIX文件系统,它就是文件输入/输出,而它的地址就是文件名。
2、套接字类型
因特网提供了两种通信机制:流(stream)和数据报(datagram),因而套接字的类型也就分为流套接字和数据报套接字。这里主要讲流套接字。
流套接字由类型SOCK_STREAM指定,它们是在AF_INET域中通过TCP/IP连接实现,同时也是AF_UNIX中常用的套接字类型。流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。
数据报套接字是在AF_INET域中,是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
3、套接字协议
只要底层的传输机制允许不止一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。通常只需要使用默认值。
二、套接字地址
每个套接字都有其自己的地址格式,对于AF_UNIX域套接字来说,它的地址由结构sockaddr_un来描述,该结构定义在头文件sys/un.h中,它的定义如下:
struct sockaddr_un{
sa_family_t sun_family;//AF_UNIX,它是一个短整型
char sum_path[];//路径名
};
对于AF_INET域套接字来说,它的地址结构由sockaddr_in来描述,它至少包括以下几个成员:
struct sockaddr_in{
short int sin_family;//AF_INET
unsigned short int sin_port;//端口号
struct in_addr sin_addr;//IP地址
};
而in_addr被定义为:
struct in_addr{
unsigned long int s_addr;
};
三、流式socket的接口及作用
socket的接口函数声明在头文件sys/types.h和sys/socket.h中。
1、创建套接字——socket系统调用
该函数用来创建一个套接字,并返回一个描述符,该描述符可以用来访问该套接字,它的原型如下:
int socket(int domain, int type, int protocol);
其中family指明协议族,type参数指明套接字类型,protocol参数应该设为某个(见下图)协议类型常值,或者设为0,以选择所给定family和type组合的系统默认值
socket函数中family和type参数的组合
2、命名(绑定)套接字——bind系统调用
bind函数把一个本地协议地址赋予一个套接字。对于网际协议,协议地址是一个ip地址和一个端口号,函数原型如下:
int bind( int socket, const struct sockaddr *address, size_t address_len);
第二个参数是一个指向特定协议的地址结构的指针,第三个参数是该地址结构的长度。
成功时返回0,失败时返回-1;
3、创建套接字队列(监听)——listen系统调用
该函数用来创建一个队列来保存未处理的请求。成功时返回0,失败时返回-1,其原型如下:
int listen(int socket, int backlog);
backlog用于指定队列的长度,等待处理的进入连接的个数最多不能超过这个数字,否则往后的连接将被拒绝,导致客户的连接请求失败。调用后,程序一直会监听这个IP端口,如果有连接请求,就把它加入到这个队列中。
4、接受连接——accept系统调用
accept函数由TCP服务器调用,用于从已完成连接队列列头返回下一个已完成连接,如果已完成连接队列为空,进程将被投入睡眠,它的原型如下:
int accept(int socket, struct sockaddr *address, size_t *address_len);
address为连接客户端的地址,参数address_len指定客户结构的长度,如果客户地址的长度超过这个值,它将会截断。
5、请求连接——connect系统调用
该系统调用用来让客户程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器。它的原型如下:
int connect(int socket, const struct sockaddr *address, size_t address_len);
socket是由socket()返回的套接口描述字,第二、三个参数分别是一个指向接口地址结构的指针和该结构体的大小。
参数socket指定的套接字连接到参数addres指定的服务器套接字。成功时返回0,失败时返回-1.
6、关闭socket——close系统调用
该系统调用用来终止服务器和客户上的套接字连接,我们应该总是在连接的两端(服务器和客户)关闭套接字
/
eg:大写字符串转小写字符串
ser.c:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<ctype.h>
4 #include<sys/socket.h>
5 #include<arpa/inet.h>
6 #include<string.h>
7
8 #define BUFF_SIZE 100
9 #define IP_LEN 16
10
11 //实现将一个字符串中所有的大写字母转成小写字母
12 void func(char* str)
13 {
14 if(str==NULL)
15 return;
16
17 char* p=str;
18 while(*p !='\0')
19 {
20 if('A'<=*p && *p<='Z')
21 *p=*p-'A'+'a';
22
23 p++;
24 }
25 }
26
27 int main()
28 {
29 struct sockaddr_in sin; //服务器IP
30 struct sockaddr_in cin; //客户端IP
31 int lfd; //监听套接字
32 int cfd; //客户端套接字
33 socklen_t len;
34 char buff[BUFF_SIZE]={0};
35 char addr_p[IP_LEN]={0};//存储字符串IP地址
36 int n;
37
38 bzero(&sin,sizeof(sin));
39 sin.sin_family=AF_INET;
40 sin.sin_port=htons(6000);
41 sin.sin_addr.s_addr=INADDR_ANY; //服务器可以接受任意地址
42
43 lfd=socket(AF_INET,SOCK_STREAM,0);
44 bind(lfd,(struct sockaddr*)&sin,sizeof(sin));
45 listen(lfd,5);
46
47 while(1)
48 {
49 int len=sizeof(cin);
50 cfd=accept(lfd,(struct sockaddr*)&cin,&len);//accept()会返回客户端的套接字
51 if(cfd==-1)
52 {
53 perror("accept error");
54 exit(0);
55 }
56 while(1)
57 {
58 n=recv(cfd,buff,BUFF_SIZE,0);
59 if(n==-1)
60 {
61 perror("recv error");
62 exit(0);
63 }
64 else if(n==0)//客户端已关闭
65 {
66 printf("one client has closed\n");
67 close(cfd);
68 exit(0);
69 }
70 inet_ntop(AF_INET,&cin.sin_addr,addr_p,sizeof(addr_p));
71 //将二进制IP转为点分十进制IP
72 printf("client:IP:%s,port:%d\n",addr_p,ntohs(cin.sin_port));
73 printf("the data from client is:%s\n",buff);
74 func(buff);
75 n=send(cfd,buff,strlen(buff)+1,0);
76 if(n==-1)
77 {
78 perror("send error");
79 exit(0);
80 }
81 }
82 if(close(cfd)==-1)
83 {
84 perror("close error");
85 exit(0);
86 }
87
88 }
89 if(close(lfd)==-1)
90 {
91 perror("close error");
92 exit(0);
93 }
94 return 0;
95 }
cli.c:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<ctype.h>
4 #include<sys/socket.h>
5 #include<arpa/inet.h>
6 #include<string.h>
7
8 #define BUFF_SIZE 100
9
10 //int main(int argc,char* argv[])
11 int main()
12 {
13 struct sockaddr_in sin;
14 int sfd;
15 char buff[BUFF_SIZE]={0};
16 char str[BUFF_SIZE]={0};
17 //char* str="Hello World";
18 //if(argc>1)
19 //str=argv[1];
20
21 bzero(&sin,sizeof(sin));
22 sin.sin_family=AF_INET;
23 sin.sin_port=htons(6000);
24 inet_pton(AF_INET,"127.0.0.1",&sin.sin_addr);
25
26 if((sfd=socket(AF_INET,SOCK_STREAM,0))==-1)
27 {
28 perror("socket error");
29 exit(0);
30 }
31 if((connect(sfd,(struct sockaddr*)&sin,sizeof(sin)))==-1)
32 {
33 perror("connect error");
34 exit(0);
35 }
36 while(1)
37 {
38 fgets(str,BUFF_SIZE,stdin);
39 str[strlen(str)-1]='\0';
40 if(strcmp(str,"end")==0)
41 {
42 break;
43 }
44
45 if((send(sfd,str,strlen(str)+1,0))==-1)
46 {
47 perror("send error");
48 exit(0);
49 }
50 if((recv(sfd,buff,BUFF_SIZE,0))==-1)
51 {
52 perror("recv error");
53 exit(0);
54 }
55 printf("recive from server:%s\n",buff);
56 bzero(str,BUFF_SIZE);
57 bzero(buff,BUFF_SIZE);
58 }
59
60 if((close(sfd))==-1)
61 {
62 perror("close error");
63 exit(0);
64 }
65
66 return 0;
67 }
运行结果: