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);//以 数字.数字.数字.数字 形式显示出来以下系统调用需要经常用到:
socket()bind()connect()listen()accpet()send()recv()sendto()recvfrom()close()shutdown()以下是部分常用的系统调用具体说明,代码有详细说明:
#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不允许发送接受操作在面向连接的协议程序中,服务器端按照以下流程执行:
调用socket()创建一个套接字调用bind()把自己绑定在一个地址上调用listen()函数侦听连接调用accept()函数接受所有引入的请求连接成功之后调用send()和recv()进行和客户机client的通信在面向连接的协议程序中,客户端按照以下流程执行:
调用socket()创建一个套接字调用bind()把自己绑定在一个地址上调用connect()函数进行连接连接成功之后调用send()和recv()进行和服务器server的通信以下是一个关于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; }具体使用方法:
首先用gcc对server.c编译,然后启动server程序另外打开一个终端,用gcc对client.c编译,然后在命令行中输入以下命令 telnet localhost 4000 ,连接之后在client终端中输入消息,即可在server中得到输出。得到以下输出结果 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次消息接受>>>如果看完了并觉得有收获的话,走过路过留个赞,磕头谢!!