最近写了一些Linux下网络编程的一些程序,做几点总结吧。
先给出客户端后服务器的一些Socket初始化的代码,以后可以直接拿来调用。
客户端Socket初始化代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <time.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <errno.h> #include <sys/time.h> #define IP "192.168.1.111" #define PORT 12346 int init_socket() { struct sockaddr_in server_addr; int err; int client_socket; client_socket=socket(AF_INET,SOCK_STREAM,0); if(client_socket<0) { printf("socket erro\n"); return ; } //设置服务器端的地址,端口等 server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=inet_addr(IP);//字符串类型转IP类型 server_addr.sin_port=htons(PORT); bzero(&(server_addr.sin_zero),8); connect(client_socket,(struct sockaddr *)&server_addr,sizeof(struct sockaddr));//连接服务器 return client_socket; }
直接调用上面的函数返回一个套接字的文件描述符,利用它便可以和服务器进行通信。
下面给出服务器socket初始化的代码
#include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <errno.h> #include <openssl/des.h> #include <sys/time.h> #include <fcntl.h> #define PORT 12346 #define BACKLOG 20 int init_server_socket() { int server_socket,server_client_socket; struct sockaddr_in server_addr; int err; //创建套接字 server_socket=socket(AF_INET,SOCK_STREAM,0); if(server_socket<0) { printf("socket error\n"); return -1; } //设置服务器端的地址,端口等 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = htons(INADDR_ANY); bzero(&(server_addr.sin_zero), 8); //将创建的套接字绑定到服务器端 err = bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)); if(err<0) { printf("bind error\n"); return -1; } //监听套接字 err=listen(server_socket,BACKLOG); if(err<0) { printf("listen error\n"); return -1; } return server_socket; }
上面的初始化函数返回了一个套接字文件描述符,我们在服务器程序中,可以写一个循环,当有客户端连接请求的时候,服务器就建立一个线程和服务器进行通信。代码如下所示:
while(1) { int addrlen=sizeof(struct sockaddr); struct sockaddr_in client_addr; //如果调用成功,将返回一个新的套接字与客户端通信 server_client_socket=accept(server_socket,(struct sockaddr*)&client_addr,&addrlen); //下面两行是将这个socket连接设置成非阻塞模式 // int flags = fcntl(server_client_socket, F_GETFL, 0); // fcntl(server_client_socket, F_SETFL, flags | O_NONBLOCK); printf("%s has connected success\n",inet_ntoa(client_addr.sin_addr)); //在这里写创建线程程序 。。。。。。 usleep(100); }
要点总结:
1、在服务器端程序要判断recv()函数的返回值,如果对方发送的数据比较的则需采用循环接收的方法,比如对方一次发了4096字节的数据,一次rrecv()可能接收不了,则需要多次接收,直到接收的总数等于4096字节,看下面的代码你就会明白
#define MAXBUFFER_SIZE 4096 int total_reieved =0; char recvbuffer[MAXBUFFER_SIZE]; while(1) { if(MAXBUFFER_SIZE==total_reieved) break; int size=recv(server_client_socket,recvbuffer+total_reieved,MAXBUFFER_SIZE-total_reieved,0); total_reieved+=size; }
2、如果发送方的程序关闭了,在接收方就会不断recv()并返回0,则我们就要关闭这个连接,防止接收方陷入死循环
3、拔网线拔掉重新插上,socket可以自动恢复连接
4、默认的连接时阻塞模式的,如果要将套接字通信设置成非阻塞模式我们可以参考下面的代码(对套接字文件描述符设置),非阻塞模式下如果读不到数据则返回-1,网络断开也是返回-1
int flags = fcntl(server_client_socket, F_GETFL, 0); fcntl(server_client_socket, F_SETFL, flags | O_NONBLOCK);
5、假如客户端程序一直发送数据,服务器程序一直接受数据,这时服务器程序关闭,则客户端程序也会关闭,这时因为服务器close一个连接时,若客户端接这发数据,系统会产生一个SIGPIPE信号,告诉进程连接已经断开了,而处理这个信号的默认动作是使进程退出,如果不想进程退出,可以把SIGPIPE设为SIG_IGN。调用函数signal(SIGPIPE,SIG_IGN);
大概就总结了这几点,欢迎批评和指正。