linux开发用什么软件socket,第一篇博客-自己的小聊天软件-linux下socket基于TCP协议聊天软件...

前言

决定写博客是受到一位前辈的影响,对自己学习的经历一种总结。回想自己七年的大学学习生涯,到现在刚工作,还是个半吊子水平,很多东西都不精,于是想系统的记录一下成长的过程,一方面当作笔记,另一方面也可以看看到底自己是个什么水平。

好了,费话说完,说正事。年初时自己学习linux下的网络编程,曾经写过一个简单的tcp聊天软件。就翻出来当作自己的处女篇吧。

简介

linux下的网络进程间通信是基于套接字接口API实现的,即socket。原型如下:

#include

int socket(int domain, int type, int protocol);与此类似的各种API接口,在程序中应用还有很多,函数原型以及具体的参数类型以及返回值,错误处理等,参考相应的manual page,文中就不做额外介绍了。

tcp协议是一种传输层协议,工作在五层网络模型的第二层,介于应用层与IP层(网络层)之间。与UDP协议不同的是,TCP是面向连接的,因此客户端与服务器程序编写稍显复杂。大致过程如下:

11309e8319860654dac1d864ce1945d0.png

程序代码地址:https://github.com/yangyinqi/chatroom

代码

首先是服务器设计,由于涉及到多客户的服务,所以选择select进行连接描述符的轮询。

int sock_init(void)

{

int serverfd, sock_len;

struct sockaddr_in server_address;

serverfd = socket(AF_INET, SOCK_STREAM, 0);

server_address.sin_family = AF_INET;

server_address.sin_addr.s_addr = htonl(INADDR_ANY);

server_address.sin_port = htons(PORTNUM);

sock_len = sizeof(server_address);

if(bind(serverfd, (struct sockaddr *)&server_address, sock_len)<0){

perror("Bind error.");

exit(EXIT_FAILURE);

}

if(listen(serverfd, 10)<0){

perror("Listen error.");

exit(EXIT_FAILURE);

}

return serverfd;

}

对socket进行初始化,设置其为IPv4因特网域,类型为面向连接的字节流。

int main(int argc, char **argv)

{

int listenfd, comfd, maxfd;

int maxi, i, com_len, client[FD_SETSIZE];

struct sockaddr_in client_address;

fd_set readfds, testfds;

int result;

char data[MAXSIZE], str[64];

ClientPtr clientlist;

memset(data, 0, MAXSIZE);

listenfd = sock_init();

clientlist = ClientListInit();//Initial the client list

maxfd = listenfd;

maxi = -1;

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

client[i] = -1;

FD_ZERO(&readfds);

FD_SET(listenfd, &readfds);

while(1){

int fd, nread;

testfds = readfds;

result = select(maxfd+1, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);

if(result < 1){

perror("server5");

exit(EXIT_FAILURE);

}

if(FD_ISSET(listenfd, &testfds)){

com_len = sizeof(client_address);

comfd = accept(listenfd, (struct sockaddr *)&client_address, &com_len);

FD_SET(comfd, &readfds);

ClientAdd("Yang", comfd,

ntohs(client_address.sin_port),

inet_ntop(AF_INET, &client_address.sin_addr, str ,sizeof(str)),

clientlist);

printf("new client: %s, port %d\n",

inet_ntop(AF_INET, &client_address.sin_addr, str ,sizeof(str)),

ntohs(client_address.sin_port));

for(i=0; i

if(client[i] < 0){

client[i] = comfd;

break;

}

}

if(i == FD_SETSIZE){

perror("too many clients");

exit(EXIT_FAILURE);

}

FD_SET(comfd, &readfds);

if(comfd > maxfd)

maxfd = comfd;

if(i>maxi)

maxi = i;

}

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

if(client[i] < 0)

continue;

if(FD_ISSET(client[i], &testfds)){

if((nread=read(client[i], data, MAXSIZE)) == 0){

close(client[i]);

FD_CLR(client[i], &readfds);

printf("client %d removed.\n", client[i]);

client[i] = -1;

}

else{

printf("client %d :%s",client[i], data);

write(client[i], data, nread);

}

}

}

}

exit(EXIT_SUCCESS);

}

本来设计了一个表来实现聊天用户名称,IP等信息的存储,但还是没有完成。

while(1)大循环里的逻辑大致是:对整个文件描述符集合进行轮询,若 监听 套接字有活动,则说明有新客户端请求连接,将信息保存后,从accept返回的fd保存在数组里。

若不是监听套接字的活动,则对保存客户端连接的套接字描述符数组进行遍历,根据read返回的值处理。

客户端:

#include "client.h"

int main(int argc, char **argv)

{

int client_len;

struct sockaddr_in client_address;

int num;

char data[MAXSIZE], readbuff[MAXSIZE];

ClientPtr local_info = (ClientPtr)malloc(sizeof(ClientNode));

//pthread_t thread_id;

void *thread_res;

int opt, error_status = 0;

if(argc < 2) {

print_notice();

exit(EXIT_FAILURE);

}

else if(argc > 3) {

printf("Too many argument");

exit(EXIT_FAILURE);

}

memset(local_info, 0, sizeof(ClientNode));

while((opt = getopt(argc, argv, ":u:n")) != -1) {

switch(opt) {

case 'u':

printf("Anonymous user.\n");

//local_info.ClientName = "Anonymous";

strcpy(local_info->ClientName, "Anonymous");

break;

case 'n':

printf("Your name: %s\n", optarg);

strcpy(local_info->ClientName, optarg);

break;

case '?':

printf("Unknown option: %c\n",optopt);

//print_notice();

error_status = 1;

break;

}

}

if(error_status) {

print_notice();

exit(EXIT_FAILURE);

}

fd_status = 0;

//memset(local_info, 0, sizeof(ClientNode));

memset(&client_address, 0, sizeof(struct sockaddr_in));

(void)signal(SIGINT, quit_sig);

client_fd = socket(AF_INET, SOCK_STREAM, 0);

client_address.sin_family = AF_INET;

client_address.sin_addr.s_addr = htonl(INADDR_ANY);

client_address.sin_port = htons(PORTNUM);

client_len = sizeof(client_address);

info_copy(local_info, &client_address);

local_info->ClientFD = client_fd;

// if((bind(client_fd, (struct sockaddr *)&client_address, client_len))<0) {

// perror("Bind error.");

// exit(EXIT_FAILURE);

// }

if((connect(client_fd, (struct sockaddr *)&client_address, client_len))<0) {

perror("Connect error.");

exit(EXIT_FAILURE);

}

if(pthread_create(&thread_id, NULL, write_thread, (void *)client_fd) != 0) {

perror("Thread creation failed.");

exit(EXIT_FAILURE);

}

while(1) {

if(fd_status) {

printf("Press enter to close");

break;

}

else {

read(client_fd, readbuff, MAXSIZE);

printf("%s", readbuff);

}

}

pthread_join(thread_id, &thread_res);

close(client_fd);

//printf("%s", (char *)thread_res);

free(local_info);

exit(EXIT_SUCCESS);

}

char *package_write(char *src)

{

return src;

}

void info_copy(ClientPtr info, struct sockaddr_in *local)

{

char addr[16];

info->ClientAddr = inet_ntop(AF_INET, &local->sin_addr, addr,sizeof(addr));

info->ClientPort = local->sin_port;

}

void print_notice()

{

printf("Option:\n\t./client -u\tConnect to the server in anonymous mode\n");

printf("\t./client -n [name]\tConnect to the server in your own name\n");

}

void *write_thread(void *arg)

{

int fd = (int)arg;

char *data_write;

data_write = (char *)malloc(MAXSIZE*sizeof(char));

//pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

//pthread_detach(pthread_self());

while(1) {

if(fd_status) {

printf("Press Enter to close");

//fflush(stdin);

break;

}//connection closed

else {

fgets(data_write, MAXSIZE, stdin);

data_write = package_write(data_write);

//pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

//pthread_testcancel();//cancle the thread when the socket closed.

send(fd, data_write, MAXSIZE, NULL);

}

}

free(data_write);

//printf("Write thread end.\n");

pthread_exit("Write thread end.\n");

}

void quit_sig(int sig)

{

printf("Press Enter to close");

// write(client_fd, client_end, 21);

fd_status = 1;

// close(client_fd);

//pthread_cancel(thread_id);

// (void)signal(SIGINT, SIG_IGN);

// exit(EXIT_SUCCESS);

}

客户端程序将发送和接受两部分分别在两个线程中进行处理。

大概的逻辑是:初始化socket以及结构体后,进行conncet,发送连接服务器的请求,返回正确后,将描述符作为参数传递给新建立的发送数据线程。相比较服务器,逻辑简单很多。至于那么多注释。。无视掉吧,想写个结束线程的东西,水平有限当时怎么也没弄出来。

后记

服务器设计还有许多思路,例如使用多线程,并发处理各个客户请求,这就需要一些线程同步机制,还有和select系统调用看起来类似,但实质上完全不同的epoll,采用回调函数的方式,对各个连接进行处理,这种方法在实际服务器开发中用的更多一些。

其实我感觉linux下的网络聊天室采用UDP套接字设计会简单一些。常用的聊天工具,比如QQ,飞秋都是基于udp协议设计自己的应用层协议的。

欢迎各路大神批评指正。最近在写一个网口传输速率的测试程序,完成后也会写成博客与大家分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值