I developed a chat server in the C language and Python language
The chat written in Python is posted here. Now I developed the chat using the C sockets and threads.
The code developed in C follows the same principles of the code in Python.
In C had to develop a double linked list to store the file descriptor of the socket, user name and nickname.
This code was developed to the discipline of Distributed Systems.
# # This program is free software; you can redistribute it and/or modify
# #it under the terms of the GNU General Public License as published by
# #the Free Software Foundation; version 2 of the License.
# # This program is distributed in the hope that it will be useful,
# #but WITHOUT ANY WARRANTY; without even the implied warranty of
# #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# #GNU General Public License for more details.
# #
# #author: Marlon Petry
# #Date: 2008/04/30
# #Function: Chat server in C sockets threads
# #
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* for FIONREAD (sigh) */
#include
#include
#include “listaDDE.h”
#include
#define MYPORT 5800 // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
#define MAXLINE 1024
#define NUM_THREADS 50
fd_set rmask;
pthread_mutex_t mutexsum;
pthread_mutex_t mutexLogin;
int fd_max;
int maxfds;
int sockfd;
fd_set read_fds;
fd_set master;
nodo *root = NULL;
static int read_cnt;
static char *read_ptr;
static char read_buf[MAXLINE];
static void *sendMensagem(void *arg);
void sigchld_handler(int s);
static void *sendALL(char msg[],int soks);
static void *sendMensagem(void *arg);
static void *newClientConnect(int arg);
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
void *sendALL(char msg[],int soks)
{
nodo *aux = getFirst();
while(aux != NULL)
{
int soc =aux->socket;
if(soc != soks)
{
if(write(soc, msg, strlen(msg)) == -1)
perror(”send”);
printf(”send %s %s\n”,msg,aux->nick);
}
aux = aux->prox;
}
}
void *sendMensagem(void *arg)
{
char *buff;
int fd = (*(int *) arg);
int nbytes;
char tmp[5000];
printf(”\nthread call %d”,fd);
newClientConnect(fd);
printf(”\n entry LOOP\n”);
nodo *cliente = busca(fd);
while(1)
{
buff = (char *) calloc( MAXLINE,sizeof(char));
if(FD_ISSET(fd,&master))
{
nbytes = recv(fd,buff, MAXLINE -1 , 0);
if(nbytes > 1)
{
if(!tmp)
{
perror(”alocation:”);
exit(-1);
}
strcat(tmp, cliente->nick);
strcat(tmp, ” >> “);
strcat(tmp, buff);
sendALL(tmp,fd);
}
else
{
if (nbytes == 0) {
printf (”servidor: socket %d desligado\n”, fd);
} else {
perror (”recv”);
}
close (fd); // BYE!!
FD_CLR( fd , &master);
removi(getFirst(),fd);
pthread_exit(NULL);
}
}
free(buff);
}
}
static void *newClientConnect(int arg)
{
int fd = arg;
pthread_t tid;
struct sockaddr_in their_addr;
char welcome[] = “Welcome Chat SistDistribuidos 0.1 \n”;
char msg1[] = “Login: “;
char msg2[] = “NickName: “;
char nome[100];
char nick[100];
int nbytes;
socklen_t sin_size;
pthread_mutex_lock (&mutexLogin);
printf(”\naccept \n”);
if (write(fd, welcome, sizeof welcome - 1) != sizeof welcome - 1) {
perror(”write:”);
exit(1);
}
printf(”write %s \n”,welcome);
usleep(1000);
if (write(fd, msg1, strlen(msg1)) != strlen(msg1)) {
perror(”write:”);
exit(1);
}
printf(”write %s \n”,msg1);
usleep(1000);
if ((nbytes = recv(fd, nome, 100,0)) < 0) {
perror("read:");
exit(1);
return;
}
nome[nbytes -1] = '\0';
printf("Read %d %s\n",strlen(nome),nome);
usleep(1000);
if (write(fd, msg2, sizeof msg2 - 1) != sizeof msg2 - 1) {
perror("write:");
exit(1);
}
if ((nbytes =recv(fd, nick, 100,0)) < 0) {
perror("read:");
exit(1);
}
nick[nbytes-1]='\0';
printf("Read %d %s\n",strlen(nick),nick);
inseri(&root,fd,nome,nick, inet_ntoa(their_addr.sin_addr));
FD_SET(fd, &master); /* listen to this socket too */
printf("end welcome \n");
pthread_mutex_unlock (&mutexLogin);
}
int main(void)
{
// listen on sock_fd, new connection on new_fd
int j,new_fd;
struct timeval waitd;
int nbytes;
char buf[256];
int fd_max; //numero maximo de descritores
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
FD_ZERO(&master);
FD_ZERO(&read_fds);
pthread_mutex_init(&mutexsum, NULL);
pthread_mutex_init(&mutexLogin,NULL);
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if ( setsockopt ( sockfd , SOL_SOCKET , SO_REUSEADDR , &yes , sizeof ( int ) ) == -1 )
{
perror ( " setsockopt " );
exit ( 1 );
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) == -1) {
perror("bind");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
FD_SET(sockfd, &master);
fd_max = sockfd;
int rc =0;
int i = 0;
int ret;
int fd;
waitd.tv_sec = 1;
waitd.tv_usec = 0;
while(1)
{
read_fds = master;
if (select (fd_max+1, &read_fds, NULL, NULL, NULL)== -1) {
perror ("select");
exit(1);
}
int addrlen = sizeof (their_addr);
if (( new_fd = accept(sockfd,&their_addr,&addrlen)) == -1) {
perror ("accept");
} else {
FD_SET ( new_fd , &master);
printf ("server : new connection %s socket %d\n",
inet_ntoa (their_addr.sin_addr),new_fd);
pthread_t tred;
rc = pthread_create( &tred, NULL,sendMensagem, (void *) &new_fd);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
}
pthread_mutex_destroy(&mutexsum);
pthread_mutex_destroy(&mutexLogin);
return 0;
}
Header Double Linked List name ListaDDE.h
# #!/usr/bin/env python
# # -*- coding: iso-8859-1 -*-
# # This program is free software; you can redistribute it and/or modify
# #it under the terms of the GNU General Public License as published by
# #the Free Software Foundation; version 2 of the License.
# # This program is distributed in the hope that it will be useful,
# #but WITHOUT ANY WARRANTY; without even the implied warranty of
# #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# #GNU General Public License for more details.
# #
# #author: Marlon Petry
# #
#include
#include
#include
#define MAXBUFFER 2048
typedef struct cel
{
int socket;
char nome[50];
char nick[50];
char host[50];
char buff[MAXBUFFER];
struct cel *ante;
struct cel *prox;
}nodo;
nodo *getFirst();
nodo *getLast();
void inseri(nodo **root, int socket,char nome[],char nick[],char host[]);
void removi(nodo *root, int socket);
void print (nodo *ini);
Implementation Double Linked List without use global variable
# #!/usr/bin/env python
# # -*- coding: iso-8859-1 -*-
# # This program is free software; you can redistribute it and/or modify
# #it under the terms of the GNU General Public License as published by
# #the Free Software Foundation; version 2 of the License.
# # This program is distributed in the hope that it will be useful,
# #but WITHOUT ANY WARRANTY; without even the implied warranty of
# #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# #GNU General Public License for more details.
# #
# #author: Marlon Petry
# #Date: 2008/04/30
#include
#include
#include
#include “listaDDE.h”
nodo *first = NULL, *last = NULL;
nodo *getFirst()
{
printf(”call getFirst()\n”);
return first;
}
nodo *getLast()
{
return last;
}
void inseri(nodo **root, int socket, char nome[],char nick[],char host[])
{
nodo *aux;
//primeira inserção
if(*root == NULL)
{
*root = (nodo *) malloc(sizeof (nodo));
if(*root == NULL)
{
perror(”erro de alocação\n”);
}
(*root)->socket = socket;
strcpy((*root)->nome , nome);
strcpy((*root)->nick , nick);
strcpy((*root)->host , host);
(*root)->prox = NULL;
(*root)->ante = NULL;
first = (*root);
last = first;
}
else
{
aux = (nodo *) malloc(sizeof (nodo));
if(aux == NULL)
{
perror(”erro de alocação\n”);
}
aux->socket = socket;
strcpy(aux->nome , nome);
strcpy(aux->nick , nick);
strcpy(aux->host , host);
aux->ante =last;
aux->prox = NULL;
last->prox = aux;
last = aux;
*root = last;
}
}
void removi(nodo *primeiro,int dado)
{
nodo *aux = primeiro;
while(aux != NULL)
{
if(dado == aux->socket && aux == first) //Remove first element
{
if(aux->prox != NULL)
{
printf(”!= null\n”);
aux->prox->ante = NULL;
first = aux->prox;
}
else
{
printf(”null\n”);
free(first);
break;
}
}
else if(dado == aux->socket && aux->prox == NULL) //remove last
{
aux->ante->prox = NULL;
last = aux->ante;
free(aux);
break;
}
else if(dado == aux->socket && aux->prox != NULL && aux->ante !=NULL) //remove middle
{
aux->ante->prox = aux->prox;
aux->prox->ante = aux->ante;
free(aux);
break;
}
aux = aux->prox;
}
}
void print (nodo *ini)
{
nodo *p = first;
while(p != NULL)
{ printf (”socket %d cliente %s\n”, p->socket,p->nome);
p = p->prox;
}
}
nodo *busca (int key)
{
nodo *p = getFirst();
int flag = 0;
while(p != NULL)
{
if(p->socket == key)
{
return p;
}
p = p->prox;
}
return NULL;
}
In order to compile this code together use the follow command
gcc -o chatserver server.c listaDDE.c -lpthread
Using
On the server
./chatserver
On the client
telnet ipServer 5800
Digg if you like it:
Development of a chat with the C sockets and Threads