一、TCP通信步骤
TCP服务器部分:
1、调用函数socket(),创建一个socket
2、设置sockaddr_in信息,如要连接服务器的IP和端口等属性
3、调用函数bind(),绑定IP地址、端口等信息到socket上
4、调用函数listen(),设置允许的最大连接数
5、调用函数accept(),等待来自客户端的连接请求
6、调用函数send() 和 recv() 或者 read() 和 write() ,收发数据
7、调用close(),关闭网络连接
TCP客户端部分:
1、调用函数socket(),创建一个socket
2、设置sockaddr_in信息,如要连接服务器的IP和端口等属性
3、调用函数connect(),连接服务器
4、调用函数send() 和 recv() 或者 read() 和 write() ,收发数据
5、调用close(),关闭网络连接
从整体上看:
服务端:
1、创建socket
int socket(int domain,int type,int protocol)
参数domain 指定使用何种的地址类型:
PF_INET / AF_INET Ipv4网络协议;
PF_UNIX / PF_LOCAL / AF_UNIX / AF_LOCAL UNIX 进程通信协议
type为传输类型:
SOCK_STREAM 提供双向连续且可信赖的数据流,即TCP。
SOCK_DGRAM 使用不连续不可信赖的数据包连接,即UDP。
protocol用来指定socket所使用的传输协议编号,通常设为0即可。
成功则返回新的socket处理代码,失败返回-1。
2、sockaddr_in 结构体信息填充
- struct sockaddr_in
- {
- short int sin_family; //网络协议类型
- unsigned short int sin_port; //端口号
- struct in_addr sin_addr; //目的地址结构体,其中有s_addr要做设定
- unsigned char sin_zero[8]; //无用字节为0即可,不用设置
- }
0、将结构体初始化
void bzero(void *s,int n)
头文件为<string.h>。bzero()会将参数s所指的内存区域前n个字节,全部设为零值。相当于调用memset((void*)s,0,size_tn);
1、sin_family:
协议类型与socket的domain相同。如:PF_INET
2、sin_port:
unsigned short int htons(unsigned short int hostshort);
htons()用来将参数指定的16位hostshort转换成网络字符顺序。返回对应的网络字符顺序。
3、sin_addr:设置的是sin_addr.s_addr
unsigned long int inet_addr(const char *cp);
inet_addr()用来将参数cp所指的网络地址字符串转换成网络所使用的二进制数字。网络地址字符串是以数字和点组成的字符串,例如:“127.0.0.1”。成功则返回对应的网络二进制的数字,失败返回-1。
unsigned long int htonl(unsigned long int hostlong);
htonl()用来将参数指定的32位hostlong 转换成网络字符顺序。返回对应的网络字符顺序。通常为INADDR_ANY。
INADDR_ANY就是指定地址为0.0.0.0的地址,表示所有地址。 一般来说,在各个系统中均定义成为0值。
3、bind信息到socket
int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
sockfd为socket函数的返回值。
my_addr 为刚才填写的结构体,要类型转换 (SA *)&myadd。
addrlen为结构体的大小,用sizeof函数算。
成功返回0,失败返回-1。
4、listen设置连接数
int listen(int sockfd,int backlog);
listen()用来设定ssockfd的最大连接数(backlog),如果连接数目达此上限则client端将收到ECONNREFUSED的错误。Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client端连线的是accept()。成功则返回0,失败返回-1。
5、accept等待连接请求
int accept(int socketfd,struct sockaddr * addr,int * addrlen);
accept()用来接受参数socketfd连线。当有连线进来时accept()会返回一个新的socket处理代码,往后的数据传送与读取就是经由新的socket处理,而原来参数socketfd能继续使用accept()来接受新的连线要求。
连线成功时,参数addr所指的结构会被系统填入远程主机的地址数据。
addrlen为scokaddr的结构长度的指针。
成功则返回新的socket处理代码,相当于文件操作符,失败返回-1。
客户端:
3、connect连接
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
用来将参数sockfd的socket连至参数serv_addr指定的网络地址。参数addrlen为sockaddr的结构长度。
成功则返回0,失败返回-1
二、举例
- //server
- #include<stdio.h>
- #include<sys/socket.h>
- #include<sys/types.h>
- #include<unistd.h>
- #include<stdlib.h>
- #include<netinet/in.h>
- #include<arpa/inet.h>
- #include<memory.h>
- int main(int argc,char *argv[]){
- struct sockaddr_in sev_addr,new_addr;
- int sockfd,new_fd=0;
- char buff[1024];
- int nbytes;
- int struct_size;
- if(argc != 2){
- printf("Usage : %s <hostname>\n",argv[0]);
- return 1;
- }
- if((sockfd=socket(PF_INET,SOCK_STREAM,0))<0){
- printf("socket create error!\n");
- return 1;
- }
- bzero(&sev_addr,sizeof(struct sockaddr_in));
- sev_addr.sin_family = PF_INET;
- sev_addr.sin_port = htons(8888);
- sev_addr.sin_addr.s_addr = inet_addr(argv[1]);
- if(bind(sockfd,(struct sockaddr*)&sev_addr,sizeof(struct sockaddr_in))<0){
- printf("bind error!\n");
- return 1;
- }
- if(listen(sockfd,5)<0){
- printf("Listen set error!\n");
- return 1;
- }
- while(1){
- struct_size=sizeof(struct sockaddr_in);
- bzero(&new_addr,sizeof(struct sockaddr_in));
- if(new_fd=accept(sockfd,(struct sockaddr*)&new_addr,&struct_size)<0){
- printf("accept error!\n");
- return 1;
- }
- printf("The %s is conneted!\n",inet_ntoa(new_addr.sin_addr));
- memset(buff,'\0',sizeof(buff));
- if((nbytes=read(new_fd,buff,1024)) == -1){ //第一次读是从服务端输入流读入的不知是什么原因?求解释
- printf("read error\n");
- return 1;
- }
- printf("read over the nbytes is %d\n",nbytes);
- buff[nbytes]='\0';
- printf("Server receive the message: %s",buff);
- close(new_fd);
- printf("exit.....\n");
- }
- }
- //client
- #include<stdio.h>
- #include<sys/socket.h>
- #include<sys/types.h>
- #include<unistd.h>
- #include<stdlib.h>
- #include<netinet/in.h>
- #include<arpa/inet.h>
- #include<memory.h>
- int main(int argc,char *argv[]){
- struct sockaddr_in clin_addr,new_addr;
- int sockfd,new_fd;
- char buff[1024];
- int nbytes;
- int struct_size;
- if(argc != 2){
- printf("Usage : %s <hostname>\n",argv[0]);
- return 1;
- }
- if((sockfd=socket(PF_INET,SOCK_STREAM,0))<0){
- printf("socket create error!\n");
- return 1;
- }
- bzero(&clin_addr,sizeof(struct sockaddr_in));
- clin_addr.sin_family = PF_INET;
- clin_addr.sin_port = htons(8888);
- clin_addr.sin_addr.s_addr = inet_addr(argv[1]);
- if(connect(sockfd,(struct sockaddr*)&clin_addr,sizeof(struct sockaddr_in))<0){
- printf("connect error!\n");
- return 1;
- }
- memset(buff,'\0',1024);
- fgets(buff,1024,stdin);
- write(sockfd,buff,strlen(buff));
- close(sockfd);
- printf("client will exit!\n");
- return 0;
- }