当前位置:首页 > 基于linux的socket多线程通信
TCP服务器端依次调用
socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、
connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样
连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。 注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
3.5、read()、write()等函数
万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:
? ? ? ? ?
read()/write() recv()/send() readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()
我推荐使用
recvmsg()/sendmsg()函数,这两个函数是最通用的I/O函数,实际上可以把上面的其它函数都替换成这两个
函数。它们的声明如下:
#include
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
#include
ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。
write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数。失败时返回-1,并设置errno变量。 在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是全部的数据。2)返回的值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)。
其它的我就不一一介绍这几对I/O函数了,具体参见man文档或者baidu、Google,下面的例子中将使用到send/recv。
3.6、close()函数
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。
#include
close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。
注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。
下面为调试正确的客户端与服务器的程序代码:欢迎参考引用: //server.c:
#include
void *rec_data(void *fd) { int client_sockfd; int n; char buff[4096]={0}; client_sockfd=*((int*)fd); for(;;)
{
char buff[4096]={0};
if((n=recv(client_sockfd,buff,MAXLINE,0)) <= 0) { printf(\ return 0; }
if(strncmp(\接收到quit时,跳出循环 { printf(\ break; }
printf(\打印收到的数据 } free(fd); close(client_sockfd); }
int main(int argc,char **argv) {
int server_sockfd; /*定义一个用于监听的套接字描述符*/ int *client_sockfd; /*定义响应套接字描述符指针变量*/ struct sockaddr_in server_address;/*三种表示套接字地址的结构之一:sockaddr_in结构,专门针对Internet通信域, 存储套接字相关的网络地址信息,例如IP地址,传输层端口号等信息*/ struct sockaddr_in client_address; int server_len,client_len; char buff[4096]={0}; /*创建套接字:流式套接字,提供全双工的面向连接的可靠的按序递交的无重复字节流通信; 创建socket过程就是创建一个套接字并返回一个整型描述符*/ server_sockfd = socket(AF_INET,SOCK_STREAM,0);// 创建一个用于监听的流式套接字 */ if(server_sockfd == -1) { printf(\ exit(1);
}
memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(6666);
/*绑定:往往在生成套接字的描述符后,要将套接字与计算机上的特定的IP地址和传输层端口号相关联, 这个过程称为绑定。一个套接口要使用一个确定的三元组网络地址信息,才能使它在网络中唯一地被标识。 将本地地址绑定到监听套接字*/ if( bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { printf(\ exit(1); }
//开始监听,并指定监听套接字请求队列的长度10 if( listen(server_sockfd, 10) == -1) { printf(\ exit(1); }
printf(\
/* 服务器主循环: 接受和处理来自客户端的连接请求 */ while(1) {
pthread_t thread;//创建不同的子线程以区别不同的客户端
client_sockfd = malloc(sizeof(*client_sockfd)); //malloc返回的是一个(void *)类型 if(client_sockfd==NULL) { printf(\ exit(1); }
client_len = sizeof(client_address);
/* 接受客户端连接请求,并生成响应套接字 */
共分享92篇相关文档