Giriş
Linux'da socket kullanabilmek için genellikle şu dosyalar dahil edilir.
Socket Handle
Winsock API'si SOCKET yapısı ile çalışıyor gibi görünüyor. Ancak altta SOCKET aslında unsigned int. Linux'ta ise socket API'si int ile çalışıyor. Ortak bir yapı için int olarak tutulabilir.
SOCKADDR
Bir çok Windows kodunda sockaddr yapısına cast etmek yerine SOCKADDR yapısına cast edildiğini görebiliriz.
Winsock'un başlatılması
Windows'ta socketleri kullanmaya başlamadan önce Winsock kütüphanesinin başlatılması gerekiyor. Kütüphaneyi kullanmak için şu dosya dahil edilir ve linklenir. Şöyle yaparız.
Şöyle yaparız.
Şöyle yaparız. Winsock sürümü olarak 2.2 veya 2.1 kullanılabilir.
Örnek - AF_INET + Family + Flag
Winsock ve Linux'ta şöyle yaparız.
Çağrı başarılı ise 0 döner, hata varsa 0'dan farklı bir değer döner. Windows'ta çağrı sonucu şöyle kontrol edilir.
Örnek - Hata kontrolü
Şöyle yaparız.
Şöyle yaparız.
Windows'ta ioctlsocket (sockfd,FIONBIO,...) ile yapılır. Linux'ta fcntl (sockfd,F_SETFL,...) ile yapılır. Çağrı başarılı ise 0 döner.
buffering mode
Windows ve Linux'ta aynıdır. setsockopt (sockfd,IP_PROTO_TCP,TCP_NODELAY,..) ile yapılır.
connect
İstemci soketlerde kullanılır.
Örnek
Şöyle yaparız.
Çağrı başarılı ise 0 döner, hata varsa 0'dan farklı bir değer döner.
close
Winsock'ta şöyle yaparız.
Şöyle yaparız.
Örnek
Şöyle yaparız.
İmzası şöyle.
TCP Socket İçin setsockopt metodu yazısına taşıdım.
setsockopt - reuse
Hata varsa -1 döner. Metoda geçerken (char*) olarak geçilebilir. Şöyle yaparız.
Şöyle yaparız.
Şöyle yaparız.
Windows'ta SHUT_RDWR sabiti yerine SD_BOTH kullanılır. Şöyle yaparız.
Şöyle yaparız.
Linux'da socket kullanabilmek için genellikle şu dosyalar dahil edilir.
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/types.h>
TCP sunucucu açmak için çağrı sırası şöylesocket -> bind(serverfd) -> listen(serverfd) -> accept(serverfd) -> recv (clientfd)
Socket Handle
Winsock API'si SOCKET yapısı ile çalışıyor gibi görünüyor. Ancak altta SOCKET aslında unsigned int. Linux'ta ise socket API'si int ile çalışıyor. Ortak bir yapı için int olarak tutulabilir.
SOCKADDR
Bir çok Windows kodunda sockaddr yapısına cast etmek yerine SOCKADDR yapısına cast edildiğini görebiliriz.
struct sockaddr_in local;
...
bind(sockfd, (SOCKADDR*)&sockinfo, sizeof(sockinfo));
SOCKADDR yerine sockaddr rahatlıkla kullanılabilir.Windows'ta socketleri kullanmaya başlamadan önce Winsock kütüphanesinin başlatılması gerekiyor. Kütüphaneyi kullanmak için şu dosya dahil edilir ve linklenir. Şöyle yaparız.
#pragma comment(lib, "ws2_32.lib")
#include <WinSock2.h>
mingw'de şöyle yaparız.x86_64-w64-mingw32-gcc test.c
-lws2_32 -o a.exe
ÖrnekŞöyle yaparız.
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
fprintf(stderr, "WSAStartup failed.\n");
}
ÖrnekŞöyle yaparız. Winsock sürümü olarak 2.2 veya 2.1 kullanılabilir.
WSADATA wasData;
int wsaRes = WSAStartup(MAKEWORD(2, 2), &wasData);
Çağrı başarısız ise 0'dan farklı bir sonuç döner. Şöyle yaparız.if(wsaRes != 0) {...}
Eğer hata olursa şöyle yazdırabiliriz. printf("Last error code was(1): %d\n", WSAGetLastError());
Uygulamadan çıkarken şöyle yapmak gerekir.WSACleanup();
Socket YaratmakÖrnek - AF_INET + Family + Flag
Winsock ve Linux'ta şöyle yaparız.
int
sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Örnek
IPv6 için şöyle yaparız.
Flag sabitleri yerine rakam da kullanılabilir. Şöyle yaparız.
AF_INET yerine yeni sabitleri de kullanabiliriz.
Açıklaması şöyle. non blocking socket ile kullanılması tavsiye ediliyor.
Yani çağrı sonucunda değeri değişiyor. Her accept çağrısından önce üçüncü parametreyi ilklendirmek gerekir. Parametre tipi olarak int veya socklen_t kullanılabilir.
Şu kod ilklendirme yapılmadığı için yanlış.
IPv6 için şöyle yaparız.
int serverSockFd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
Örnek - AF_INET + Family + FlagFlag sabitleri yerine rakam da kullanılabilir. Şöyle yaparız.
int sockfd = socket(AF_INET , SOCK_STREAM , 0);
Şöyle yaparız.SOCKET socfd = socket(AF_INET, SOCK_STREAM, NULL);
Örnek - PF_INET + Family + FlagAF_INET yerine yeni sabitleri de kullanabiliriz.
int sockfd
= ::socket(PF_INET, SOCK_STREAM, 0);
Windows'ta işlemin başarılı olduğu şöyle kontrol edilir.if (INVALID_SOCKET == sockfd) {...}
Invalid socket -1 değerine sahiptir.#define INVALID_SOCKET (SOCKET)(~0)
accept metoduAçıklaması şöyle. non blocking socket ile kullanılması tavsiye ediliyor.
Üçüncü parametrenin açıklaması şöyle.There may not always be a connection waiting after a SIGIO is delivered or select(2), poll(2), or epoll(7) return a readability event because the connection might have been removed by an asynchronous network error or another thread before accept() is called. If this happens, then the call will block waiting for the next connection to arrive. To ensure that accept() never blocks, the passed socket sockfd needs to have the O_NONBLOCK flag set (see socket(7)).
The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.'
Şu kod ilklendirme yapılmadığı için yanlış.
struct sockaddr_in clientinfo;
int clientsockSize;
for(;;)
{
int child_fd = accept(sockfd, addr, &addrlen);
...
}
Şöyle yaparız.struct sockaddr_in clientinfo; int clientsockSize = sizeof(clientinfo); int clientfd = accept(sockfd,
(struct sockaddr
*)&clientinfo, &clientsockSize);
Çağrı başarılı ise -1'den farklı bir değer döner, hata varsa -1 döner. Windows'ta çağrı sonucu şöyle kontrol edilir.
SOCKADDR_IN client_sock = accept(...);
if (c_socket == INVALID_SOCKET)
{
printf("Error %d", WSAGetLastError());
}
Çağrı bağlanan soketi döndüğü için bu değeri saklamak gerekir. Şu kod dönülen socket fd'yi saklamadığı için yanlıştır.if(accept(...) >= 0) {
...
}
bindstruct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(1234); //
local.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr
*)&local, sizeof(local));
if (SOCKET_ERROR == bind(...)) {...}
Socket Error şu değere sahiptir.
#define SOCKET_ERROR (-1)
Şöyle yaparız.
int bind_success = bind(...);
if (bind_success != 0)
{
printf("Error %d", WSAGetLastError());
}
Örnek - Hata kontrolüŞöyle yaparız.
if( bind(...) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
blocking modeWindows'ta ioctlsocket (sockfd,FIONBIO,...) ile yapılır. Linux'ta fcntl (sockfd,F_SETFL,...) ile yapılır. Çağrı başarılı ise 0 döner.
buffering mode
Windows ve Linux'ta aynıdır. setsockopt (sockfd,IP_PROTO_TCP,TCP_NODELAY,..) ile yapılır.
connect
İstemci soketlerde kullanılır.
Örnek
Şöyle yaparız.
struct sockaddr_in remote;
remote.sin_family = AF_INET;
remote.sin_port = htons(2000);
remote.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(sockfd, (struct sockaddr*) &remote
, sizeof(remote));
Örnek - Hata kontrolüÇağrı başarılı ise 0 döner, hata varsa 0'dan farklı bir değer döner.
int connection_success = connect(...);
if (connection_success != 0)
{
printf("Error %d", WSAGetLastError());
}
Eğer socket non-blocking ise asenkron çalışmak gerekir. Bu durumda select() metodunu kullanırsak ve soketimizi write seti'ine verirsek bağlantının başarılı olduğunu şöyle anlarız. Windows'ta ise except_fd setini kullanmak gerekir.// call this select() has indicated that (fd) is ready-for-write because
// (fd)'s asynchronous-TCP connection has either succeeded or failed.
// Returns true if the connection succeeded, false if the connection failed.
// If this returns true, you can then continue using (fd) as a normal
// connected/non-blocking TCP socket. If this returns false, you should
// close(fd) because the connection failed.
bool FinalizeAsyncConnect(int fd)
{
#if defined(__FreeBSD__) || defined(BSD)
// Special case for FreeBSD7, for which send() doesn't do the trick
struct sockaddr_in junk;
socklen_t length = sizeof(junk);
memset(&junk, 0, sizeof(junk));
return (getpeername(fd, (struct sockaddr *)&junk, &length) == 0);
#else
// For most platforms, the code below is all we need
char junk;
return (send(fd, &junk, 0, 0L) == 0);
#endif
}
close
Winsock'ta şöyle yaparız.
::closesocket(
sockfd);
Linux'ta şöyle yaparız.::close(
sockfd);
listen metodu
İmzası şöyle.
Şöyle yaparız. Hata varsa -1 döner.
Windows'ta şöyle yaparız.
Çağrı başarılı ise 0 döner, hata varsa 0'dan farklı bir değer döner. Şöyle yaparız.
Örnekİmzası şöyle.
int listen (int socket, int backlog);
backlog için açıklama şöyleThere is a thing called "backlog" which is a queue in which incoming connections temporarily stay until you have had a chance to accept them. It exists precisely so as to allow you to spend some time doing stuff between successive calls to accept.Eğer back log değeri 1 ise, 1 tane handshake yapılır ancak ikincisi reddedilir. Açıklaması şöyle.
The length of the backlog is a parameter to the listen() method. If the value that you specify is too small, and a large number of simultaneous requests to connect are received, you risk losing some connections due to appearing busy.
So if your backlog is 1 and your server thread isn't waiting on accept, than means that an incoming client connection will at least establish the TCP handshake. If you attempt to have two pending connections when the backlog queue is 1, the second client connection will likely timeout or be refused if the server code isn't actively invoking accept to promote those connections out of the backlog and into sockets.POSIX stadandardına göre backlog parameretresi tavsiye niteliğinde. Açıklaması şöyle.
Farklı işletim sistemleri farlı davranabilir. Linux için açıklama şöyleThe backlog argument provides a hint to the implementation which the implementation shall use to limit the number of outstanding connections in the socket's listen queue.
ÖrnekThe behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection requests. The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog. When syncookies are enabled there is no logical maximum length and this setting is ignored. See tcp(7) for more information.
Şöyle yaparız. Hata varsa -1 döner.
listen(sockfd , 3);
ÖrnekWindows'ta şöyle yaparız.
listen(sockfd, SOMAXCONN)
Örnek - Hata kontrolüÇağrı başarılı ise 0 döner, hata varsa 0'dan farklı bir değer döner. Şöyle yaparız.
int listen_success = listen(...);
if (listen_success != 0)
{
printf("Error %d ", WSAGetLastError());
}
Şöyle yaparız.if (listen(sockfd, SOMAXCONN) == SOCKET_ERROR)
{
...
}
read metodu
Örnek
Şöyle yaparız.
Şöyle yaparız.
int readString (int fd, char *buf, int bufsize)
{
int m = 0; /* total number of bytes received */
int n = 0; /* number of bytes received in a single read */
while (m < bufsize - 1)
{
n = read(fd, buf + m, bufsize - m - 1);
if (n == -1) /* socket read error */
{
perror("Error reading socket");
break;
}
if (n == 0) /* socket is closed */
break;
m += n;
}
if (m >= bufsize)
perror("Error buffer overflow");
buf[m] = '\0';
return m;
}
recv metoduŞöyle yaparız.
char message [1024];
int messageLen = recv(sockfd
, message, sizeof(message) +1, 0);
Eğer 0 yerine MSG_WAITALL bayrağını verirsek belirtilen byte sayısını okuyuncaya kadar çağrı bloke olur.Örnek
Şöyle yaparız.
int read_size;
char buf[2000];
//Receive a message from client
while( (read_size = recv(sockfd, buf, sizeof(buf) , 0)) > 0 )
{
...
}
send metoduİmzası şöyle.
Şöyle yaparız.ssize_t send(int sockfd, const void *buffer, size_t length, int flags);
char* message = "hel";
send(sockfd, &message, sizeof(message), 0);
setsockopt TCP Socket İçin setsockopt metodu yazısına taşıdım.
setsockopt - reuse
Hata varsa -1 döner. Metoda geçerken (char*) olarak geçilebilir. Şöyle yaparız.
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(char *)&reuse, sizeof(int))) {
Metoda geçerken (void*) olarak geçilebilir. Arada ne fark var bilmiyorum. Şöyle yaparız.int optVal = 1;
const socklen_t optLen = sizeof(optVal);
int rtn = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &optVal,
optLen);
if (rtn < 0)
{
perror("Error");
}
setsockopt - send timeoutŞöyle yaparız.
struct timeval timeout;
//Time outs on socket operations
timeout.tv_sec = 10;
timeout.tv_usec = 0;
//Set time-out parameters
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof(timeout)) == -1) {
...error...
}
setsockopt - sigpipe
Yazarken SIGPIPE sinyali atar. Eğer SIGPIPE atmayı kapatırsak EPIPE değerine bakmak gerekir. Şöyle yaparız.
Yazarken SIGPIPE sinyali atar. Eğer SIGPIPE atmayı kapatırsak EPIPE değerine bakmak gerekir. Şöyle yaparız.
int on = 1;
setsockopt(socketfd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(int));
shutdown metodu
İmzası şöyle.int shutdown(int s, int how); // s is socket descriptor
Açıklaması şöyle.Örnekint shutdown(int socket, int how);...The shutdown() function shall cause all or part of a full-duplex connection on the socket associated with the file descriptor socket to be shut down.The shutdown() function takes the following arguments:
socket Specifies the file descriptor of the socket. how Specifies the type of shutdown. The values are as follows:
- SHUT_RD Disables further receive operations.
- SHUT_WR Disables further send operations.
- SHUT_RDWR Disables further send and receive operations.
...
Şöyle yaparız.
shutdown(socketfd, SHUT_WR);
ÖrnekWindows'ta SHUT_RDWR sabiti yerine SD_BOTH kullanılır. Şöyle yaparız.
shutdown(socketfd, SD_BOTH);
write metoduŞöyle yaparız.
int writeBuffer (int fd, char *buf, int len)
{
int m = 0;
int n = 0;
while (m < len)
{
n = write(fd, buf + m, len - m);
if (n == -1) {
perror("Error socket send");
break;
}
if (n == 0) /* socket is closed */
break;
m += n;
}
return m;
}
Hiç yorum yok:
Yorum Gönder