赞
踩
socket套接字网络通信代码在最下面,可自取
套接字有三种类型:
①流式套接字(SOCK_STREAM)
②数据报套接字(SOCK_DGRAM)
③原始套接字
个人理解:流式套接字采用TCP连接方式,数据报套接字采用UDP连接方式
struct sockaddr,该结构用来存储套接字地址
数据定义如下:
struct sockaddr{
unsigned short sa_family;//address族,一般使用AF_INET
char sa_data[14];//包含一些远程电脑的地址、端口和套接字的数目
}
②但是,该结构在套接字中一般用struct sockaddr_in结构来替代struct sockaddr,该结构定义如下:
sruct sockaddr_in{//后缀in代表Internet
short int sin_family;//address族,一般使用AF_INET
unsigned short int sin_port;//端口号
struct in_addr sin_addr;//Internet地址
unsigned char sin_zero[8];//添0,目的是和struct sockaddr的大小保持一致
}
③struct in_addr用于因特网地址,结构定义如下:
struct in_addr{
unsigned long s_addr;
}
网络字节顺序转换函数
由于不同主机的存储有大端字节和小端字节的区别,而在网络传输需要进行统一顺序,这就要用到了网络转换函数。
注意:把数据发送到Internet之前,一定要把它的字节顺序从主机字节顺序转换到网络字节顺序。
htons() -- Host to NetWork Short 主机字节顺序转换为网络字节顺序
htonl() -- Host to NetWork Long 主机字节顺序转换为网络字节顺序
ntohs() -- NetWork to Host Short 网络字节顺序转换为主机字节顺序
ntohl() -- NetWork to Host Long 网络字节顺序转换为主机字节顺序
inet_addr(ip),把ip转换为网络字节,该函数得到的返回值已经是网络字节顺序了,不用再使用htons()函数进行转换了。
struct sockaddr_in my_addr;
my_addr.sin_addr.s_addr = inet_addr("192.111.69.52");
inet_ntoa()用于将in_addr类型的数据转换为一个字符串指针,并返回;
char *p;
p = inet_ntoa(my_addr.sin_addr);
printf("address: %s",p);//以 数字.数字.数字.数字 形式显示出来
以下系统调用需要经常用到:
以下是部分常用的系统调用具体说明,代码有详细说明:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
socket()系统调用用于取得套接字描述符;
其中domain一般设置为"AF_INET";
type则对应套接字的类型,"SOCK_STREAM"或"SOCK_DGRAM";
protocol只需要设置为0;
该函数返回一个你以后可以使用的套接字描述符
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,struct sockaddr* my_addr, int addrlen);
该函数用于将socket套接字描述符绑定一个机器上的端口;
sockfd为socket()函数返回的套接字描述符
my_addr是一个指向struct sockaddr的指针,一般可以将sockaddr_in对象的地址利用强制类型转换放在此处当做参数,如((struct sockaddr*)&my_addr);
addrlen可以设置为sizeof(struct sockaddr);
注意:使用bind绑定端口号时候,最好将端口参数设置大一点,小于1024的端口号都是被保留下来作为系统使用端口的,你可以使用1024以上的任何端口,一直到65535;
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,struct sockaddr *serv_addr,int addrlen)
sockfd和addrlen的取值和bind()中类似
serv_addr应该是一个存储远程计算机的IP地址和端口信息的结构;
最好对返回值进行检查,如果返回错误值-1表示发生了错误(无法连接到远程主机等);
#include <sys/socket.h>
int listen(int sockfd,int backlog);
listen()是等待别人连接,进行系统侦听请求的函数
sockfd是套接字描述符;
backlog是本地能够等待的未连接的最大数目,推荐使用SOMAXCONN宏;
执行完listen之后,将socket从主动套接字变为被动套接字
#include <sys/socket.h>
int accept(int sockfd, void* addr, int* addrlen);
当有客户从别的地方尝试使用connect()函数来连接某个已经在listen()的端口时候,accept()将返回一个新的套接字描述符,
代表这个连接。而最开始listen()的那个套接字描述符依旧在原来的端口上listen();
sockfd是正在listen()的套接字描述符;
addr指向着从远程连接过来的计算机的信息,是一个struct sockaddr_in结构的指针;
addrlen是本地的一个整型数值,其大小应该是sizeof(struct sockaddr_in);
从已连接队列返回第一个连接,如果已连接队列为空则阻塞
send()和recv()是基本的SOCK_STREAM套接字流进行通讯的函数 #include <sys/types.h> #include <sys/socket.h> int send(int sockfd, const void* msg, int len, int flags); sockfd表示已经建立连接的套接字描述符,如accept()返回的套接字描述符; msg为发送信息的地址; len是发送信息的长度; flags为发送标记,一般设0; send()函数的返回值为真正发送信息的长度; int recv(int sockfd,void* buf,int len, unsigned int flags); sockfd表示已经建立连接的套接字描述符,如accept()返回的套接字描述符; buf是一个指针,指向能够存储数据的缓冲区; len是缓存区最大尺寸; flags为接受标记,一般设0; recv()函数的返回值为真正接受信息的长度;
sendto()和recvfrom()函数是无连接UDP通讯时候使用的,此处不做详细介绍;
close(int sockfd);
使用标准的关闭文件的函数来对套接字描述符进行关闭操作;
执行close()函数之后,套接字将不会允许进行读写操作;
int shutdown(int sockfd, int how)
如果想对套接字的关闭进行进一步操作的话,可以使用shutdown()函数;
sockfd为套接字描述符;
how为控制方式变量:0不允许数据接受操作,1不允许数据发送操作,2不允许发送接受操作
在面向连接的协议程序中,服务器端按照以下流程执行:
在面向连接的协议程序中,客户端按照以下流程执行:
以下是一个关于cs模型中服务器端和客户端的通信的代码详解:
客户端server.c
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #define MYPORT 4000 #define BACKLOG 10 #define MAXDATASIZE 100 int main() { char buf[MAXDATASIZE]; int sock_fd,sock_new; //定义套接字描述符 //定义套接字地址对象 struct sockaddr_in my_addr; struct sockaddr_in their_addr; //初始化本地连接地址 my_addr.sin_family = AF_INET; my_addr.sin_port = htons(MYPORT); my_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(my_addr.sin_zero),8); //套接字描述符初始化 if((sock_fd = socket(AF_INET,SOCK_STREAM,0)) == -1) { perror("socket error!"); exit(1); } //绑定套接字描述符 if((bind(sock_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))) == -1) { perror("bind error!"); exit(1); } //listen()函数监听 if((listen(sock_fd,BACKLOG)) == -1) { perror("listen error!"); exit(1); } //accept()主循环 //进行listen()之后的套接字进行accept(),只有accept()之后才能进行收发数据 while(1) { //sin_size用于accept()中最后一个参数 int sin_size = sizeof(struct sockaddr_in); if((sock_new = accept(sock_fd,(struct sockaddr*)&their_addr,&sin_size)) == -1) { perror("accpet error!"); continue; } printf("server:got connetion form %s\n",inet_ntoa(their_addr.sin_addr)); if(fork() == 0)//fork==0表示此处是子线程里面,子线程用于处理远处的连接 { //发送数据 if((send(sock_new,"Hello!\n",7,0)) == -1) { perror("send error!\n"); close(sock_new); exit(0); } //接受数据 int len; int i = 0; //char str[] = "------------------------------->>>>>\n"; printf("等待第%d次消息接受>>>\n",i+1); while(1) { i++; if((len = recv(sock_new,buf,MAXDATASIZE,0)) == -1) { perror("recv error!"); exit(0); } //接受数据打印 printf("第%d次收到消息,内容如下:\n",i); buf[len] = '\0'; printf("%s",buf); bzero(buf,MAXDATASIZE); printf("等待第%d次消息接受>>>\n",i+1); } close(sock_new); } } while(waitpid(-1,NULL,WNOHANG) > 0); exit(0); }
客户端client.c
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <unistd.h> #define MYPORT 4000//端口号码 #define MAXDATASIZE 100//最大缓存空间 int main(int argc, char * argv[]) { int sock_fd,numbytes; char buf[MAXDATASIZE]; struct hostent* he; struct sockaddr_in their_addr; if(argc != 2) { fprintf(stderr,"usage:client hostname\n"); exit(1); } if((he = gethostbyname(argv[1])) == NULL) { herror("gethostbyname"); exit(1); } //使用socket()函数获取可用的套接字 if((sock_fd = socket(AF_INET,SOCK_STREAM,0) == -1)) { perror("socket error!"); exit(1); } //对their_addr进行参数设置 their_addr.sin_family = AF_INET; their_addr.sin_port = htons(MYPORT); //这个地方在琢磨一下!! their_addr.sin_addr = *((struct in_addr*)he->h_addr); bzero(&(their_addr.sin_zero),8);//置零 //connect()函数用于连接设定好的their_addr地址 if((connect(sock_fd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr))) == -1) { perror("connect error!"); exit(1); } //利用recv()函数接受数据 if((numbytes = recv(sock_fd,buf,MAXDATASIZE,0)) == -1) { perror("recv error!"); exit(1); } buf[numbytes] = '\0'; printf("Recvived:%s\n",buf); int i = 0; //利用send()发送数据 while(1) { i++; printf("第%d次输入数据:\n",i); //fflush(stdout); scanf("%s",buf); if(send(sock_fd,buf,sizeof(buf),0) == -1) { perror("send error!"); exit(1); } bzero(buf,MAXDATASIZE); printf("第%d次成功发送\n",i); fflush(stdout); } //关闭打开的socket套接字 close(sock_fd); return 0; }
具体使用方法:
t@t:~/Desktop/c_s/client$ telnet localhost 4000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello!
hello world!
you are the best!
t@t:~/Desktop/c_s/server$ ./server
server:got connetion form 127.0.0.1
等待第1次消息接受>>>
第1次收到消息,内容如下:
hello world!
等待第2次消息接受>>>
第2次收到消息,内容如下:
you are the best!
等待第3次消息接受>>>
如果看完了并觉得有收获的话,走过路过留个赞,磕头谢!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。