做一个极(jian)简(lou)的文字聊天的服务器,明天做客户端。
浅尝
这次主要用前面的socket编程,只是这次用的是ipv4的TCP/IP协议来做的。主要用socket
bind
listen
accept
send
recv
这几个系统调用,是一个最经典的用法了。值得注意的是MSG_NOSIGNAL
和MSG_DONTWAIT
这两个flag,一个是要求send
失败时不要发送信号终止进程,另一个是要求recv
不阻塞。
代码演示
#include <unistd.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
int send_to_client(int fd, char *msg, ssize_t size){
ssize_t s = send(fd, msg, size, MSG_NOSIGNAL);
if (s != size){
close(fd);
return -1;
}
return 0;
}
int main(int argc, char **argv){
int fd, client_fd[2];
struct sockaddr_in server_addr;
int online_nr = 0;
ssize_t size;
int line = 0;
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(1234);
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
line = __LINE__;
goto ERROR;
}
if (bind(fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in))){
line = __LINE__;
goto ERROR_CLOSE_FD;
}
if (listen(fd, 2)){
line = __LINE__;
goto ERROR_CLOSE_FD;
}
printf("0 users online.\n");
while (online_nr < 2){
socklen_t len = sizeof(struct sockaddr_in);
if ((client_fd[online_nr] = accept(fd, NULL, &len)) < 0){
line = __LINE__;
goto ERROR_CLOSE_FD;
}
if (online_nr == 0){
char greeting_string[] = "Please wait for your friend...\n";
if (send_to_client(client_fd[0], greeting_string, sizeof(greeting_string))){
continue;
}
online_nr++;
}
else if (online_nr == 1){
char str[] = "Your friend is online now.\n";
if (send_to_client(client_fd[0], str, sizeof(str))){
online_nr--;
continue;
}
if (send_to_client(client_fd[1], str, sizeof(str))){
continue;
}
online_nr++;
}
printf("%d users online.\n", online_nr);
}
while (online_nr >= 2){
int i;
char recvbuf[512];
for (i = 0; i < 2; ++i){
char leave_string[] = "Your friend is offline now, bye!\n";
size = recv(client_fd[i], recvbuf, sizeof(recvbuf), MSG_DONTWAIT);
if (size <= 0){
if (errno != EAGAIN){
if (send_to_client(client_fd[!i], leave_string, sizeof(leave_string))){
goto ERROR_CLOSE_FD;
}
}
else{
continue;
}
}
else{
struct timeval tv;
char tmp[1024];
gettimeofday(&tv, NULL);
recvbuf[sizeof(recvbuf) - 1] = '\0';
snprintf(tmp, sizeof(tmp), "[%lus:%luus]%d:\n\t%s\n",tv.tv_sec, tv.tv_usec, i, recvbuf);
if (send_to_client(client_fd[0], tmp, sizeof(tmp))){
if (send_to_client(client_fd[1], leave_string, sizeof(leave_string))){
goto ERROR_CLOSE_FD;
}
}
if (send_to_client(client_fd[1], tmp, sizeof(tmp))){
if (send_to_client(client_fd[0], leave_string, sizeof(leave_string))){
goto ERROR_CLOSE_FD;
}
}
}
}
}
return 0;
ERROR_CLOSE_FD:
close(fd);
ERROR:
printf("error@%d, %s\n", line, strerror(errno));
exit(EXIT_FAILURE);
}