您的位置:首页 > 理论基础 > 计算机网络

select—linux c language —TCP broadcast—chat

2016-02-01 00:00 471 查看
server.c

点击(此处)折叠或打开

// gcc server.c -o server

// indent -npro -kr -i8 -ts8 -sob -l280 -ss -ncs -cp1 *

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include "wrap.h"

#define MAXLINE 80

#define SERV_PORT 8020

int main(int argc, char **argv)

{

int i, maxi, maxfd, listenfd, connfd, sockfd;

int nready, client[FD_SETSIZE]; //client [FD_SETSIZE] 存放有数据请求的客户端;

ssize_t n;

fd_set rset, allset;

char buf[MAXLINE];

char str[INET_ADDRSTRLEN];

socklen_t cliaddr_len;

struct sockaddr_in cliaddr, servaddr;

listenfd = Socket(AF_INET, SOCK_STREAM, 0); //socket()打开一个网络通讯端口,返回一个套接字描述符给listenfd;

bzero(&servaddr, sizeof(servaddr)); //首先将结构体清零;

servaddr.sin_family = AF_INET; //设置地址类型为AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //转换ip地址字节序,网络地址为INADDR_ANY,这个宏表示本地任意IP地址

servaddr.sin_port = htons(SERV_PORT); //转换端口的字节序。到目前为止,结构体servaddr的设置已经完毕;

int opt = 1;

setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //允许创建端口号相同但IP地址不同的多个socket描述符

Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //将所监听的端口号与服务器的地址、端口绑定;

Listen(listenfd, 20); //listen()声明服务器处于监听状态,并且最多允许有20个客户端处于连接待状态;

maxfd = listenfd; //将所监听的最大的套接字描述符赋给maxfd;

maxi = -1;

for (i = 0; i < FD_SETSIZE; i++)

client[i] = -1; //for循环将client[i]的值设为-1,client[i]在下文中用来保存套接字描述符,这样可以把建立数据请求的端口赋给最前面的client[i];

FD_ZERO(&allset); //将allset套接字描述符集清空。

FD_SET(listenfd, &allset); //向allset套接字描述符集中添加服务器所监听到的端口(即listenfd所接受到的请求);

for (;;) { //for 用于循环接受有数据请求,要与服务器交互的client的端口(用select这个系统调用);

rset = allset; // 把allset套接字描述符集的内容赋给rset;

nready = select(maxfd + 1, &rset, NULL, NULL, NULL); // 调用select将rset有效的套接字描述符的个数给nready;

if (nready < 0) //select调用出错时,会返回一个负数给nready。该语句判断select是否调用成功。

perr_exit("select error");

if (FD_ISSET(listenfd, &rset)) { //判断listenfd所接受到的客户端的请求是否在rset集合中,这是一个监听到的客户端与所监听客户端中有数据请求的客户端的一个比对,测试该数据请求的客户端是否在监听的队列中。

cliaddr_len = sizeof(cliaddr); //把cliaddr结构体的长度赋给cliaddr_len,作为缓冲区的长度。

connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //接受客户端的连接请求,与客户端建立连接。

printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); //打印客户端的ip地址和端口号。

for (i = 0; i < FD_SETSIZE; i++) //for循环,i作为client的下标,表示放有数据请求的客户端的最小索引

if (client[i] < 0) {

client[i] = connfd;

break; //把有数据请求的客户端的套接字描述符放置到client[i]中最靠前的位置。

}

if (i == FD_SETSIZE) { //若i以达到最大值FD_SETSIZE,则表示有数据请求的客户端已达到FD_SETSIZE

fputs("too many clients\n", stderr);

exit(1);

}

FD_SET(connfd, &allset); //向allset套接字描述符集中添加与服务器建立连接并有数据请求的客户端端口;

if (connfd > maxfd)

maxfd = connfd; //若此时建立并有数据请求的客户端已大于原来的套接字描述符最大值则用connfd从置maxfd;

if (i > maxi)

maxi = i; //maxi 表示已用的 client[i]的最大索引

if (--nready == 0)

continue; //若--nready为0,则表示当前的套接字描述符集中只有listenfd这个监听的描述符,没有客户端的数据请求端口,则进行下一轮的select循环;

}

for (i = 0; i <= maxi; i++) { //for循环处理有数据请求的客户端;

if ((sockfd = client[i]) < 0) //把client[i]中存放的客户端的套接字描述符赋给sockfd。若小于0,表示这个client[i]中没有套接字描述符。

continue; //继续执行for循环,查找数据请求的客户端;

if (FD_ISSET(sockfd, &rset)) { //测试sockfd是否在rset这个描述符集中;

if ((n = Read(sockfd, buf, MAXLINE)) == 0) { // 读入客户端的数据,若n等于0表示客户端已经关闭了连接;

Close(sockfd); //客户端关闭连接了,服务器也关闭与客户端相应的连接;

FD_CLR(sockfd, &allset); //清空与客户端连接的套接字描述符在allset集中;

client[i] = -1; //同时将放置这个客户端套接字的数组位置设为-1,用来存放下一次的客户端数据请求的描述符;

} else { //若n不为0,则处理客户端的数据请求;

for (i = 0; i <= maxi; i++)

Write(client[i], buf, n); // 把 buf写回与服务器相连的每一个的客户

}

if (--nready == 0)

break; //再次判断nready,若--nready为0,则表示当前的套接字描述符集中只有listenfd这个监听的描述符,没有客户端的数据请求端口,则进行下一轮的select循环;

}

}

}

}

client.c

点击(此处)折叠或打开

// gcc client.c -o client

// indent -npro -kr -i8 -ts8 -sob -l280 -ss -ncs -cp1 *

#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include "wrap.h"

#define MAXLINE 80

#define SERV_PORT 8020

int main(int argc, char *argv[])

{

struct sockaddr_in servaddr;

char buf[MAXLINE];

int sockfd, n;

sockfd = Socket(AF_INET, SOCK_STREAM, 0); //socket()打开一个网络通讯端口,返回一个套接字描述符给sockfd;

bzero(&servaddr, sizeof(servaddr)); //首先将结构体清零;

servaddr.sin_family = AF_INET; //设置地址类型为AF_INET;

inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); // 由服务器端IP 转化为网络字节序保存在servaddr.sin_addr

servaddr.sin_port = htons(SERV_PORT); //转换端口的字节序。

Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //与服务器建立链接;

while (1) {

pid_t pid;

char *message;

int n;

pid = fork(); //调用fork()创建一个子进程;

if (pid < 0) { //调用fork时出错;

perror("fork failed");

exit(1);

}

if (pid == 0) { //若pid == 0则是子进程

n = Read(sockfd, buf, MAXLINE); //把服务器上的数据读到buf中;

if (n == 0) //若n等于0表示服务器已经关闭了连接;

printf("the servre has closed.\n");

else //即n>0,从服务器上读数据成功;

Write(STDOUT_FILENO, buf, n); //把buf中的数据写到显示器上;

} else { //若pid >0则是父进程

fgets(buf, MAXLINE, stdin); //用fgets从键盘读数据到buf中;

Write(sockfd, buf, strlen(buf)); //把buf中的数据写给服务器;

}

}

Close(sockfd); //关闭与服务器建立的链接;

return 0;

}

wrap.h

点击(此处)折叠或打开

#include <unistd.h>

#include <stdlib.h>

#include <errno.h>

#include <sys/socket.h>

void perr_exit(const char *s)

{

perror(s);

exit(1);

}

int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr)

{

int n;

again:

if ((n = accept(fd, sa, salenptr)) < 0) {

if ((errno == ECONNABORTED) || (errno == EINTR))

goto again;

else

perr_exit("accept error");

}

return n;

}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)

{

if (bind(fd, sa, salen) < 0)

perr_exit("bind error");

}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen)

{

if (connect(fd, sa, salen) < 0)

perr_exit("connect error");

}

void Listen(int fd, int backlog)

{

if (listen(fd, backlog) < 0)

perr_exit("listen error");

}

int Socket(int family, int type, int protocol)

{

int n;

if ((n = socket(family, type, protocol)) < 0)

perr_exit("socket error");

return n;

}

ssize_t Read(int fd, void *ptr, size_t nbytes)

{

ssize_t n;

again:

if ((n = read(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

ssize_t Write(int fd, const void *ptr, size_t nbytes)

{

ssize_t n;

again:

if ((n = write(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

void Close(int fd)

{

if (close(fd) == -1)

perr_exit("close error");

}

ssize_t Readn(int fd, void *vptr, size_t n)

{

size_t nleft;

ssize_t nread;

char *ptr;

ptr = vptr;

nleft = n;

while (nleft > 0) {

if ((nread = read(fd, ptr, nleft)) < 0) {

if (errno == EINTR)

nread = 0;

else

return -1;

} else if (nread == 0)

break;

nleft -= nread;

ptr += nread;

}

return n - nleft;

}

ssize_t Writen(int fd, const void *vptr, size_t n)

{

size_t nleft;

ssize_t nwritten;

const char *ptr;

ptr = vptr;

nleft = n;

while (nleft > 0) {

if ((nwritten = write(fd, ptr, nleft)) <= 0) {

if (nwritten < 0 && errno == EINTR)

nwritten = 0;

else

return -1;

}

nleft -= nwritten;

ptr += nwritten;

}

return n;

}

static ssize_t my_read(int fd, char *ptr)

{

static int read_cnt;

static char *read_ptr;

static char read_buf[100];

if (read_cnt <= 0) {

again:

if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {

if (errno == EINTR)

goto again;

return -1;

} else if (read_cnt == 0)

return 0;

read_ptr = read_buf;

}

read_cnt--;

*ptr = *read_ptr++;

return 1;

}

ssize_t Readline(int fd, void *vptr, size_t maxlen)

{

ssize_t n, rc;

char c, *ptr;

ptr = vptr;

for (n = 1; n < maxlen; n++) {

if ((rc = my_read(fd, &c)) == 1) {

*ptr++ = c;

if (c == '\n')

break;

} else if (rc == 0) {

*ptr = 0;

return n - 1;

} else

return -1;

}

*ptr = 0;

return n;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: